﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml;
using SHomeWorkshop.LunarMind.Converts;
using SHomeWorkshop.LunarTools;
using SHomeWorkshop.LunarMind.Commands;
using SHomeWorkshop.LunarMind.Adorners;
using System.Windows.Documents;
using System.Windows.Media.Imaging;
using SHomeWorkshop.LunarMind.Dialogs;
using SHomeWorkshop.LunarMind.TestEditor;
using LunarSF.SHomeWorkshop.LunarMind;
using System.Text;
using System.Windows.Media.Animation;
using System.Timers;
using System.Windows.Threading;
using System.Diagnostics;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor;
using Microsoft.Win32;
using SHomeWorkshop.LunarMind.LMETools;

namespace SHomeWorkshop.LunarMind
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();

            Globals.MainWindow = this;

            //初始化后台文档需要放到Window_Loaded事件中。
            //这是为了布局。

            foreach (CommandBinding cb in Commands.CmdBindings.MainWindowCmdBindingsList)
            {
                this.CommandBindings.Add(cb);
                this.tbxCaret.CommandBindings.Add(cb);
            }

            foreach (CommandBinding cb in Commands.CmdBindings.ScrollViewerCmdBindingsList)
            {
                this.mainScrollViewer.CommandBindings.Add(cb);
            }

            modifingsList = new ModifingsList(this);
            this.isModified = false;

            Canvas.SetZIndex(start, 1500);
            Canvas.SetZIndex(end, 1500);
            Canvas.SetZIndex(startCtrl, 1500);
            Canvas.SetZIndex(endCtrl, 1500);
            Canvas.SetZIndex(startCtrlLine, 1400);
            Canvas.SetZIndex(endCtrlLine, 1400);

            ListInCommonUseImages();

            pageAdorner = new PageAdorner(mainCanvas);

            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.mainCanvas);
            if (adornerLayer != null)
            {
                adornerLayer.Add(pageAdorner);
            }
            else
            {
                MessageBox.Show("未找到装饰层！");
            }

            Canvas.SetZIndex(selRectangle, int.MaxValue);

            //初始化截图VisualBrush.
            this.selectedBoxFillBrush.Visual = this.mainCanvas;
            this.selRectangle.Fill = this.selectedBoxFillBrush;

            //初始化演示区装饰器
            this.slideAdorner = new SlideAdorner(this.mainScrollViewer) { Visibility = System.Windows.Visibility.Collapsed, };
            this.baseDecorator.AdornerLayer.Add(this.slideAdorner);

            //将用于标志鼠标位置的齿轮标记置于最底层。
            Canvas.SetZIndex(mousePointer, int.MinValue);
            Canvas.SetZIndex(selectedTitlePointer, int.MinValue);

            //初始化配置文件管理器。
            //this.configManager = new XmlConfigManager(Globals.pathOfUserDirectory + "config.xml");
            //放在这里会出错。改到第一次调用时再初始化好了。

            workspaceManager = new WorkspaceManager(this);
        }

        /// <summary>
        /// 用于管理配置文件。
        /// </summary>
        private XmlConfigManager configManager;

        /// <summary>
        /// 配置文件管理器。
        /// </summary>
        public XmlConfigManager ConfigManager
        {
            get
            {
                if (this.configManager == null)
                {
                    this.configManager = new XmlConfigManager(Globals.pathOfUserDirectory + "config.xml");
                }
                return this.configManager;
            }
        }

        private SlideAdorner slideAdorner;

        public bool IsPresentating
        {
            get
            {
                return slideAdorner.Visibility == System.Windows.Visibility.Visible;
            }
        }

        /// <summary>
        /// 列出所有可用图像（常用图像）。
        /// </summary>
        private void ListInCommonUseImages()
        {
            string inCommonUseImagesRootPath = Globals.pathOfInCommonUseImages;
            ListInCommonUseImages(inCommonUseImagesRootPath);

            //支持双图像库。
            inCommonUseImagesRootPath = Globals.installedPath + "常用图像";
            ListInCommonUseImages(inCommonUseImagesRootPath);
        }

        private void ListInCommonUseImages(string inCommonUseImagesRootPath)
        {
            if (System.IO.Directory.Exists(inCommonUseImagesRootPath))
            {
                string[] inCommonUseImagesSubPaths = System.IO.Directory.GetDirectories(inCommonUseImagesRootPath);

                //如何排序？
                Array.Sort<string>(inCommonUseImagesSubPaths);

                foreach (string inCommonUseImagesSubPath in inCommonUseImagesSubPaths)
                {
                    tvInCommonUseImages.Items.Add(new ImageItem(inCommonUseImagesSubPath));
                }

                if (tvInCommonUseImages.Items.Count > 0)
                {
                    TreeViewItem item = tvInCommonUseImages.Items[0] as TreeViewItem;
                    if (item != null)
                    {
                        item.ExpandSubtree();
                    }
                }
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //载入学生名册列表，应与PresentationWindow.xaml.cs文件中Window_Loaded事件处理方法中的这段代码同步。
            cmboxStudentsListFilenames.Items.Clear();

            string studentListDirectoryPath = Globals.pathOfStudentsList;
            if (System.IO.Directory.Exists(studentListDirectoryPath))
            {
                var directoryInfo = new System.IO.DirectoryInfo(studentListDirectoryPath);
                var fileInfos = directoryInfo.GetFiles();

                foreach (var fi in fileInfos)
                {
                    cmboxStudentsListFilenames.Items.Add(new ComboBoxItem() { Content = fi.Name, });
                }

                string selectedStudentListItemText = Globals.MainWindow.ConfigManager.Get("SelectedStudentListShortFileName");

                if (selectedStudentListItemText != null && selectedStudentListItemText != string.Empty)
                {
                    ComboBoxItem selitem = null;
                    foreach (var i in cmboxStudentsListFilenames.Items)
                    {
                        ComboBoxItem cbi = i as ComboBoxItem;
                        if (cbi != null && (cbi.Content.ToString() == selectedStudentListItemText))
                        {
                            cmboxStudentsListFilenames.SelectedItem = cbi;
                            selitem = cbi;
                            break;
                        }
                    }

                    if (selitem == null)
                    {
                        if (cmboxStudentsListFilenames.Items.Count > 0)
                        {
                            cmboxStudentsListFilenames.SelectedIndex = 0;
                        }
                    }
                }
                else
                {
                    if (cmboxStudentsListFilenames.Items.Count > 0)
                    {
                        cmboxStudentsListFilenames.SelectedIndex = 0;
                    }
                }
            }

            //这里不能使用try catch
            Rect bounds = Properties.Settings.Default.StartWindowLocationSetting;
            if (bounds != null && (bounds.Left != 0 || bounds.Top != 0))
            {
                if (bounds.Width == 0) bounds.Width = 800;
                if (bounds.Height == 0) bounds.Height = 560;

                this.Top = bounds.Top;
                this.Left = bounds.Left;

                // Restore the size only for a manually sized thisdow.
                if (this.SizeToContent == SizeToContent.Manual)
                {
                    this.Width = bounds.Width;
                    this.Height = bounds.Height;
                }
            }

            if (System.IO.Directory.Exists(Globals.pathOfUserDirectory) == false)
            {
                try
                {
                    System.IO.Directory.CreateDirectory(Globals.pathOfUserDirectory);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("未能创建用户目录。原因是：\r\n" + ex.Message, Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }

            RecentFileItem.ReadRecentFilesFromXmlFile();

            //尽可能将根标题置中
            TryToScrollRootTitleAtCenter();//易出错

            this.mainCanvas.Focus();

            //自动返回上次的缩放比例

            var zoomText = ConfigManager.Get("Zoom");
            if (string.IsNullOrEmpty(zoomText) == false)
            {
                try
                {
                    double zoom = double.Parse(zoomText);
                    sliderOfCanvasScale.Value = zoom;
                }
                catch (Exception)
                {
                    sliderOfCanvasScale.Value = 0;
                }
            }
            else
            {
                sliderOfCanvasScale.Value = 0;
            }

            Globals.MainWindow.TryToScrollRootTitleAtCenter();

            timer.Tick += timer_Tick;

            //读取上次如何呈现填空题中各填空项答案
            var showWholeTitleFillBlanks = ConfigManager.Get("ShowWholeTitleFillBlanks");

            if (showWholeTitleFillBlanks == "True")
            {
                miShowWholeTitleFillBlanks.IsChecked = true;
            }
            else miShowWholeTitleFillBlanks.IsChecked = false;

            var fillBlankMode = ConfigManager.Get("FillBlankMode");
            ckxForceFillBlankView.IsChecked = (fillBlankMode == "True");

            var autoAskForFillBlanks = ConfigManager.Get("AutoAskForFillBlanks");
            miAutoAskForFillBlanks.IsChecked = (autoAskForFillBlanks == "True");

            //读取和显示文档内容必须在最后，因为界面选项会影响文档内容呈现。

            this.Title += (Globals.cmdParameterString == null) ? "" : Globals.cmdParameterString;

            if (string.IsNullOrWhiteSpace(Globals.cmdParameterString))
            {
                InitilizeDocument();
            }
            else
            {
                if (System.IO.File.Exists(Globals.cmdParameterString))
                {
                    if (Commands.OpenDocumentCommand.ReadDocument() == false)
                    {
                        InitilizeDocument();
                    }
                }
                else
                {
                    InitilizeDocument();
                }
            }

            this.mainScrollViewer.Focus();
            TryScrollTitle(this.RootTitle, true);
        }

        internal void InitilizeDocument()
        {
            //初始化后台文档。
            this.mainXmlDocument.LoadXml(Properties.Resources.InitDocument);
            BuildTitlesAndLines();
            RefreshTitleAndLinesLocation();
        }

        private ViewMode view = ViewMode.Default;

        public ViewMode View
        {
            get { return view; }
            set
            {
                view = value;
                if (this.mainXmlDocument != null &&
                    this.mainXmlDocument.DocumentElement != null)
                {
                    this.mainXmlDocument.DocumentElement.SetAttribute("ViewMode", value.ToString());
                }

                RefreshTitleAndLinesLocation();
            }
        }

        /// <summary>
        /// 其实就是调用RefreshTitleAndLinesLocation()方法。
        /// </summary>
        internal void RefreshView()
        {
            RefreshTitleAndLinesLocation();
        }

        /// <summary>
        /// 创建新文档或打开文档后调用。
        /// </summary>
        internal void TryToScrollRootTitleAtCenter()
        {

            // 此方法未考虑缩放。故用另一方法取代之。
            //    Title root = RootTitle;
            //    if (root == null) return;

            //    // ScrollViewer的verticalOffset，指的是ScrollViewer的内容的顶边（一般内容高度大于ScrollViewer的可视区域才会用到ScrollViewer）到ScrollViewer顶边的距离。
            //    // 这个距离值取正值。verticalOffset值越大，ScrollViewer中用户能看到的就越内容底部。
            //    double vOffset = root.LeftTop.Y + outBorder.Margin.Top + outBorder.BorderThickness.Top + outBorder.Padding.Top + root.ActualHeight / 2 -
            //        (mainScrollViewer.ActualHeight - SystemParameters.HorizontalScrollBarHeight) / 2;
            //    mainScrollViewer.ScrollToVerticalOffset(vOffset);

            //    double hOffset = root.LeftTop.X + outBorder.Margin.Left + outBorder.BorderThickness.Left + outBorder.Padding.Left + root.ActualWidth / 2
            //        - (mainScrollViewer.ActualWidth - SystemParameters.VerticalScrollBarWidth) / 2;
            //    mainScrollViewer.ScrollToHorizontalOffset(hOffset);

            TryScrollTitle(this.RootTitle, true);
        }

        static MainWindow()
        {
            scaleConverter = new ScaleConverter();
            scaleValueConverter = new ScaleValueConverter();
            halfTextSizeConverter = new HalfTextSizeConverter();
            isUnableConverter = new IsUnableConverter();
        }

        #region 转换器

        public static ScaleConverter scaleConverter;

        public static ScaleValueConverter scaleValueConverter;

        public static HalfTextSizeConverter halfTextSizeConverter;

        /// <summary>
        /// 被禁用图片使用这个转换器。
        /// 注意，要把表示按钮禁用的图片放在ZIndex的表层，挡住表示可用的图片。
        /// </summary>
        public static IsUnableConverter isUnableConverter;

        #endregion

        private string documentBackgroundBrushText = "White"; //"";默认值改为LightGray，否则撤销操作会出错。
        /// <summary>
        /// 为string.Empty时显示默认花纹背景。
        /// </summary>
        public string DocumentBackgroundBrushText
        {
            get { return documentBackgroundBrushText; }
            set
            {
                documentBackgroundBrushText = value;

                this.mainXmlDocument.DocumentElement.SetAttribute("DocumentBackground", value);

                if (value == null || value == string.Empty)
                {
                    mainCanvas.Background = (ImageBrush)this.mainCanvas.TryFindResource("DocumentBackground");

                    //浅花纹背景太花哨，不如改成浅灰色为默认色——更养眼点。2014年12月30日10时40分。
                    //但为保持兼容，这里的代码不能更改。
                    //Brush bsh = ColorPicker.GetBrushByName("LightGray");
                    //if (bsh != null) { mainCanvas.Background = bsh; }
                }
                else
                {
                    Brush bsh = ColorPicker.GetBrushByName(value);
                    if (bsh != null)
                    {
                        mainCanvas.Background = bsh;
                    }
                }

                if (this.RootTitle != null) this.RootTitle.RefreshTitleBackground();  //2014年12月30日，默认情况下，根标题背景色同文档背景色。

                foreach (var e in this.mainCanvas.Children)
                {
                    Relation r = e as Relation;
                    if (r != null)
                    {
                        r.RefreshTextBackground();
                    }

                    Title t = e as Title;
                    if (t != null)
                    {
                        t.ReadTitleTextFromXmlData();
                    }
                }
            }
        }

        private string fullPathOfDiskFile = null;

        public string FullPathOfDiskFile
        {
            get { return fullPathOfDiskFile; }
            set
            {
                fullPathOfDiskFile = value;
                this.Title = Globals.AppName + "-" + fullPathOfDiskFile;
            }
        }

        /// <summary>
        /// 默认文本色为黑色，将来可以考虑支持自定义2014年3月27日。
        /// </summary>
        public Brush DefaultFontColor
        {
            get { return Brushes.Black; }
        }

        /// <summary>
        /// 取根标题的文本为默认文件短名（不含路径）。
        /// </summary>
        public string GetDefaultFileName()
        {
            string defFileName = "";
            Title root = this.RootTitle;

            if (root != null)
            {
                var sb = new StringBuilder();

                OutputTitleText(root, "", ref sb, false);

                char[] spaces = new char[] { ' ', '　', '\t', '\r', '\n', '.' };
                //防止“..主编辑区..”和“..文本块..”啥的被当成文件名。

                char[] invalidateChars = new char[] { '\\', '/', ':', '*', '?', '"', '<', '>', '|', };

                string rootText = sb.ToString().TrimStart(spaces).TrimEnd(spaces).Replace("\r", "").Replace("\n", "");

                //Windows系统的文件名中不能包括：\/:*?"<>|
                if (rootText.Contains("\r") == false &&
                    rootText.Contains("\n") == false &&
                    rootText.Contains("\\") == false &&
                    rootText.Contains("/") == false &&
                    rootText.Contains(":") == false &&
                    rootText.Contains("*") == false &&
                    rootText.Contains("?") == false &&
                    rootText.Contains("\"") == false &&
                    rootText.Contains("<") == false &&
                    rootText.Contains(">") == false &&
                    rootText.Contains("|") == false)
                {
                    if (rootText.Length <= 24)
                    {
                        defFileName = rootText;
                    }
                    else
                    {
                        defFileName = rootText.Substring(0, 24);
                        //只取前１０个字符。否则可能过长。
                        //对于ANSI版本的api，文件名长度最大不能超过MAX_PATH ，
                        //其实就是260个字节；
                        //对于Unicode版本，最大长度可以达到32767个宽字符。
                    }
                }
                else
                {
                    defFileName = rootText;
                }
            }
            return defFileName;
        }

        public bool isPrepareInsertPictureBox = false;

        public bool isPrepareInsertCommentOrLinkTitle = false;

        public bool isPrepareInsertFixedTitle = false;

        public bool isPrepareSnapshot = false;

        private bool isModified = false;

        /// <summary>
        /// 当前文档是否被修改。
        /// </summary>
        public bool IsModified
        {
            get { return isModified; }
            set
            {
                isModified = value;
                if (isModified)
                {
                    tbIsModified.Text = "【未保存】";
                    tbIsModified.Foreground = Brushes.DarkRed;
                }
                else
                {
                    if (string.IsNullOrEmpty(FullPathOfDiskFile) == true ||
                        System.IO.File.Exists(FullPathOfDiskFile) == false)
                    {
                        tbIsModified.Text = "〖未修改〗";
                    }
                    else
                    {
                        tbIsModified.Text = "〖已保存〗";
                    }
                    tbIsModified.Foreground = Brushes.White;
                }
            }
        }

        private List<Title> selectedTitlesList = new List<Title>();

        /// <summary>
        /// 当前选中的公式列表。
        /// </summary>
        public List<Title> SelectedTitlesList
        {
            get
            {
                return selectedTitlesList;
            }
        }

        public Title MainSelectedTitle
        {
            get
            {
                foreach (Title t in SelectedTitlesList)
                {
                    if (t.IsMainSelectedTitle) return t;
                }

                return null;
            }
        }

        private TitleImage selectedTitleImage = null;

        /// <summary>
        /// 标题中的图片。选中任何一个图片都会令所有被选取的标题取消“被选取”状态。
        /// 且仅可以选取一个图片。——与标题不同：图片不允许重复选取。
        /// 
        /// ★注意：赋予null时不会清除标题选取状态。
        /// </summary>
        public TitleImage SelectedTitleImage
        {
            get { return selectedTitleImage; }
            set
            {
                if (selectedTitleImage != null && selectedTitleImage != value)
                {
                    selectedTitleImage.IsSelected = false;
                }

                selectedTitleImage = value;

                if (value != null)
                {
                    //清除所有被选中的标题。
                    ClearAllFromSelectedTitleList();

                    if (SelectedRelation != null) SelectedRelation = null;

                    if (SelectedComment != null) SelectedComment = null;

                    if (SelectedPictureBox != null) SelectedPictureBox = null;

                    if (SelectedRelation != null) SelectedRelation = null;
                    if (SelectedComment != null) SelectedComment = null;
                }
            }
        }

        private Comment selectedComment = null;

        public Comment SelectedComment
        {
            get { return selectedComment; }
            set
            {
                if (selectedComment != null && selectedComment != value)
                {
                    selectedComment.IsSelected = false;
                }

                selectedComment = value;

                if (value != null)
                {
                    ClearAllFromSelectedTitleList();

                    if (SelectedRelation != null) SelectedRelation = null;

                    if (SelectedTitleImage != null) SelectedTitleImage = null;

                    if (SelectedPictureBox != null) SelectedPictureBox = null;

                    selectedComment.IsSelected = true;

                    rotateSlider.Value = selectedComment.RotateAngle;

                    selectedComment.RefreshTbxCaret();
                }
            }
        }

        private PictureBox selectedPictureBox = null;

        public PictureBox SelectedPictureBox
        {
            get { return selectedPictureBox; }
            set
            {
                if (selectedPictureBox != null && selectedPictureBox != value)
                {
                    selectedPictureBox.IsSelected = false;
                }

                selectedPictureBox = value;

                if (value != null)
                {
                    ClearAllFromSelectedTitleList();

                    if (SelectedRelation != null) SelectedRelation = null;

                    if (SelectedTitleImage != null) SelectedTitleImage = null;

                    if (SelectedComment != null) SelectedComment = null;

                    selectedPictureBox.IsSelected = true;

                    rotateSlider.Value = selectedPictureBox.RotateAngle;
                }
            }
        }

        public Title GetTitleByID(string ID)
        {
            if (mainCanvas.Children.Count <= 0) return null;

            foreach (UIElement ue in mainCanvas.Children)
            {
                Title t = ue as Title;
                if (t != null && t.ID == ID) return t;
            }

            return null;
        }

        public Title GetTitleByGlobalIndex(int globalIndex)
        {
            if (mainCanvas.Children.Count <= 0) return null;

            foreach (UIElement ue in mainCanvas.Children)
            {
                Title t = ue as Title;
                if (t != null && t.GlobalIndex == globalIndex) return t;
            }

            return null;
        }

        public PictureBox GetPictureBoxByID(string ID)
        {
            if (ID == null || ID.Length <= 0) return null;

            foreach (UIElement ue in mainCanvas.Children)
            {
                PictureBox pb = ue as PictureBox;
                if (pb == null) continue;

                if (pb.ID != null && pb.ID.Length > 0 && pb.ID == ID) return pb;
            }

            return null;
        }

        public Comment GetCommentByID(string ID)
        {
            if (ID == null || ID.Length <= 0) return null;

            foreach (UIElement ue in mainCanvas.Children)
            {
                Comment c = ue as Comment;
                if (c == null) continue;

                if (c.ID != null && c.ID.Length > 0 && c.ID == ID) return c;
            }

            return null;
        }

        public FixedTitle GetFixedTitleByID(string ID)
        {
            if (ID == null || ID.Length <= 0) return null;

            foreach (UIElement ue in mainCanvas.Children)
            {
                FixedTitle ft = ue as FixedTitle;
                if (ft == null) continue;

                if (ft.ID != null && ft.ID.Length > 0 && ft.ID == ID) return ft;
            }

            return null;
        }

        public Relation GetRelationByID(string ID)
        {
            foreach (UIElement ue in mainCanvas.Children)
            {
                Relation r = ue as Relation;
                if (r == null) continue;

                if (r.ID != null && r.ID.Length > 0 && r.ID == ID) return r;
            }

            return null;
        }

        private Title rootTitle = null;

        /// <summary>
        /// ★★★★★注意：只找一次。因此，在打开新文档后，必须调用ReNullRootTitle()这个方法。
        /// 否则此属性值会是上一个已关闭的文档的rootTitle！！！！
        /// 必须禁止RootTitle被删除、剪切。
        /// </summary>
        public Title RootTitle
        {
            get
            {
                if (rootTitle == null)
                {
                    if (mainCanvas == null) return null;

                    foreach (UIElement ue in mainCanvas.Children)
                    {
                        Title t = ue as Title;
                        if (t == null) continue;

                        if (t.HigherLevelTitleID == null) return t;
                    }

                    return null;
                }
                else return rootTitle;
            }
        }

        /// <summary>
        /// 在打开文档后，必须调用此方法。
        /// </summary>
        public void ReNullRootTitle()
        {
            this.rootTitle = null;
        }

        #region INotifyPropertyChanged接口成员的实现。

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(object sender,
            PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sender, e);
            }
        }

        #endregion

        private XmlDocument mainXmlDocument = new XmlDocument();

        /// <summary>
        /// 代表整个文档数据的Xml文档对象。
        /// </summary>
        public XmlDocument MainXmlDocument
        {
            get { return mainXmlDocument; }
        }

        private PageAdorner pageAdorner;

        public void RefreshPageAdorner()
        {
            if (pageAdorner != null)
            {
                pageAdorner.InvalidateVisual();
            }
        }

        private PaperInfo pageSetting = Globals.defPaperInfo;

        /// <summary>
        /// 纸张信息。（页面设置）
        /// </summary>
        public PaperInfo PageSetting
        {
            get { return pageSetting; }
            set { pageSetting = value; }
        }

        private Orientation pageDirection = Orientation.Horizontal;

        /// <summary>
        /// 这个值由Canvas的宽、高作比较而得来。
        /// </summary>
        public Orientation PageDirection
        {
            get
            {
                return pageDirection;
            }

            set
            {
                pageDirection = value;
            }
        }

        /// <summary>
        /// 根据读取到的Xml文档的文本内容来构造前台文档内容。
        /// 这里由递归实现。
        /// </summary>
        public void BuildTitlesAndLines()
        {
            try
            {
                this.mainCanvas.Children.Clear();
                this.ReNullRootTitle();

                this.selectedTitlesList.Clear();
                //先清空。

                if (this.mainXmlDocument == null || this.mainXmlDocument.DocumentElement == null) return;


                XmlAttribute attrDocBack = this.mainXmlDocument.DocumentElement.GetAttributeByName("DocumentBackground");

                if (attrDocBack == null)
                {
                    mainCanvas.Background = (ImageBrush)this.mainCanvas.TryFindResource("DocumentBackground");
                    //花纹背景太花哨，默认情况下，新建文档改浅灰色背景更养眼些。2014年12月30日10时54分。
                    //但为操持兼容性，这里的代码不能改。
                    //mainCanvas.Background = Brushes.LightGray;
                }
                else
                {
                    if (attrDocBack.Value == string.Empty)
                    {
                        mainCanvas.Background = (ImageBrush)this.mainCanvas.TryFindResource("DocumentBackground");
                        //花纹背景太花哨，默认情况下，新建文档改浅灰色背景更养眼些。2014年12月30日10时54分。
                        //但为保持兼容性，这里的代码不能改。
                        //mainCanvas.Background = Brushes.LightGray;
                    }
                    else
                    {
                        Brush bsh = ColorPicker.GetBrushByName(attrDocBack.Value);
                        if (bsh != null)
                        {
                            mainCanvas.Background = bsh;
                        }
                    }
                }

                XmlAttribute attrViewMode = this.mainXmlDocument.DocumentElement.GetAttributeByName("ViewMode");
                if (attrViewMode != null)
                {
                    this.view = (ViewMode)Enum.Parse(typeof(ViewMode), attrViewMode.Value);
                }
                else
                {
                    this.view = ViewMode.Default;
                }

                XmlAttribute attrDefaultFontSize = this.mainXmlDocument.DocumentElement.GetAttributeByName("DefaultFontSize");
                if (attrDefaultFontSize != null)
                {
                    this.defaultFontSize = int.Parse(attrDefaultFontSize.Value);
                }
                else
                {
                    this.defaultFontSize = 15;
                }
                RefreshDefaultFontSizeMenuItemsState();

                XmlAttribute attrDocumentPadding = this.mainXmlDocument.DocumentElement.GetAttributeByName("DocumentPadding");
                if (attrDocumentPadding != null)
                {
                    this.documentPadding = (Thickness)Globals.ThicknessConverter.ConvertFrom(attrDocumentPadding.Value);
                }
                else
                {
                    this.documentPadding = new Thickness(Globals.BlankAreaWidthMax * 2);
                }

                XmlNode titleSetNode = this.mainXmlDocument.DocumentElement.SelectSingleNode("TitleSet");

                if (titleSetNode == null)
                {
                    this.mainXmlDocument.DocumentElement.AppendXml("<TitleSet><Title><Text>..中心标题..</Text></Title></TitleSet>");
                    titleSetNode = this.mainXmlDocument.DocumentElement.SelectSingleNode("TitleSet");
                }

                if (titleSetNode != null)
                {
                    XmlNodeList titleNodes = titleSetNode.ChildNodes;
                    if (titleNodes.Count <= 0)
                    {
                        titleSetNode.AppendXml("<Title><Text>..中心标题..</Text></Title>");
                    }

                    foreach (XmlNode tNode in titleNodes)
                    {
                        XmlAttribute attrIsFixed = tNode.GetAttribute("IsFixed");

                        Title nTitle;

                        if (attrIsFixed != null && attrIsFixed.Value.ToLower() == "true")
                        {
                            nTitle = new FixedTitle(this, null);
                        }
                        else
                        {
                            nTitle = new Title(this);
                        }

                        nTitle.XmlData = tNode;
                        mainCanvas.Children.Add(nTitle);
                    }
                }

                RefreshTitleAndLinesLocation();//必须先布局所有标题，否则无法布局关系。

                XmlNode relationSetNode = this.mainXmlDocument.DocumentElement.SelectSingleNode("RelationSet");
                if (relationSetNode != null)
                {
                    XmlNodeList relationNodes = relationSetNode.ChildNodes;
                    if (relationNodes.Count > 0)
                    {
                        foreach (XmlNode node in relationNodes)
                        {
                            XmlAttribute attrStartID = node.GetAttribute("StartTitleID");
                            XmlAttribute attrEndID = node.GetAttribute("EndTitleID");
                            if (attrStartID != null && attrEndID != null)
                            {
                                Title startTitle = GetTitleByID(attrStartID.Value);
                                Title endTitle = GetTitleByID(attrEndID.Value);
                                if (startTitle != null && endTitle != null)
                                {
                                    Relation newRelation = new Relation(this, startTitle, endTitle);
                                    newRelation.XmlData = node;

                                    mainCanvas.Children.Add(newRelation);
                                }
                            }
                        }
                    }
                }

                //此处情况特殊。不能在尾部调用RefreshTitleAndLinesLocation()方法。
                //实际上RefreshTitleAndLinesLocation()方法末尾有这几行代码。
                foreach (UIElement ue in mainCanvas.Children)
                {
                    Relation r = ue as Relation;
                    if (r != null)
                    {
                        r.RefreshLocation();
                    }
                }

                //再读入所有Comment
                XmlNode commentSet = this.mainXmlDocument.DocumentElement.SelectSingleNode("CommentSet");
                if (commentSet != null)
                {
                    foreach (XmlNode node in commentSet.ChildNodes)
                    {
                        XmlAttribute attrMasterTitleID = node.GetAttribute("MasterTitleID");
                        if (attrMasterTitleID != null)
                        {
                            Title masterTitle = this.GetTitleByID(attrMasterTitleID.Value);
                            if (masterTitle != null)
                            {
                                Comment newComment = new Comment(this, masterTitle);
                                newComment.XmlData = node;
                                mainCanvas.Children.Add(newComment);
                                mainCanvas.Children.Add(newComment.ConnectLine);
                                newComment.RefreshLoaction();
                            }
                        }
                    }
                }

                //再读入所有PictureBox
                XmlNode pbSet = this.mainXmlDocument.DocumentElement.SelectSingleNode("PictureBoxSet");
                if (pbSet != null)
                {
                    foreach (XmlNode node in pbSet.ChildNodes)
                    {
                        XmlAttribute attrMasterTitleID = node.GetAttribute("MasterTitleID");
                        if (attrMasterTitleID != null)
                        {
                            Title masterTitle = this.GetTitleByID(attrMasterTitleID.Value);
                            if (masterTitle != null)
                            {
                                PictureBox newPb = new PictureBox(this, masterTitle);
                                newPb.XmlData = node;
                                mainCanvas.Children.Add(newPb);
                                mainCanvas.Children.Add(newPb.ConnectLine);
                                newPb.RefreshLoaction();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("　　文档未能顺利构建。异常信息如下：\r\n" + ex.Message + "\r\n" + ex.StackTrace,
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 获取当前编辑器中所有标题（按 GlobalsIndex 排序）。
        /// </summary>
        /// <returns></returns>
        public List<Title> GetAllTitles()
        {
            if (mainCanvas.Children.Count <= 0) return null;

            var list = new List<Title>();

            foreach (var element in mainCanvas.Children)
            {
                if (element is Title title)
                {
                    list.Add(title);
                }
            }

            list.Sort(new GlobalIndexComparer());

            return list;
        }



        public void RefreshTitleAndLinesLocation()
        {
            for (int i = mainCanvas.Children.Count - 1; i >= 0; i--)
            {
                Path path = mainCanvas.Children[i] as Path;
                if (path != null)
                {
                    mainCanvas.Children.RemoveAt(i);
                }
            }
            //先清理掉上次画的所有线。

            mainCanvas.InvalidateArrange();
            mainCanvas.UpdateLayout();

            //计算所有带填空的标题的数目。
            int fillBlankTitleCount = 0;
            int titlesCount = 0;
            foreach (var item in mainCanvas.Children)
            {
                Title t = item as Title;
                if (t == null) continue;

                titlesCount++;
                if (t.IsFillBlankExist) fillBlankTitleCount++;
            }
            tbkFillBlankTitlesCount.Text = fillBlankTitleCount.ToString() + "/" + titlesCount;

            Title rootTitle = this.RootTitle;
            if (rootTitle == null) return;

            List<Title> subRootTitlesList = rootTitle.GetLowerLevelTitles();

            if (subRootTitlesList.Count <= 0)
            {
                //只需要直接将Root置于正中即可。

                mainCanvas.Width = rootTitle.ActualWidth + Globals.BlankPaddingOfMainCanvas * 2;
                mainCanvas.Height = rootTitle.ActualHeight + Globals.BlankPaddingOfMainCanvas * 2;

                Point leftTop = new Point((mainCanvas.Width - rootTitle.ActualWidth) / 2,
                    (mainCanvas.Height - rootTitle.ActualHeight) / 2);
                rootTitle.SetLeftTop(leftTop.X, leftTop.Y);

                Canvas.SetLeft(rootTitle, leftTop.X);
                Canvas.SetTop(rootTitle, leftTop.Y);
            }
            else
            {
                switch (view)
                {
                    case ViewMode.HorizontalList:
                        {
                            LocateTitlesByHorizontalListView(rootTitle, subRootTitlesList); break;
                        }
                    case ViewMode.VerticalList:
                        {
                            LocateTitlesByVerticalListView(rootTitle, subRootTitlesList); break;
                        }
                    default:
                        {
                            LocateTitlesByDefaultView(rootTitle, subRootTitlesList); break;
                        }
                }
            }

            foreach (UIElement ue in mainCanvas.Children)
            {
                Relation r = ue as Relation;
                if (r != null)
                {
                    r.RefreshLocation();
                }
                else
                {
                    Comment c = ue as Comment;
                    if (c != null)
                    {
                        c.RefreshLoaction();
                        //c.RefreshSummary();//这行会造成无限循环或递归。
                        if (c.IsSummary)
                        {
                            c.SummaryAdorner.InvalidateVisual();
                        }
                    }
                    else
                    {
                        PictureBox p = ue as PictureBox;
                        if (p != null)
                        {
                            p.RefreshLoaction();
                        }
                        else
                        {
                            FixedTitle ft = ue as FixedTitle;
                            if (ft != null)
                            {
                                ft.RefreshLoaction();
                            }
                        }
                    }
                }
            }

            Title mainSelected = MainSelectedTitle;
            if (mainSelected != null)
            {
                mainSelected.RefreshEditorTitleDraggingHandler();
            }

            //最后，重绘所有标题的折叠装饰器、外边框。
            foreach (UIElement ue in mainCanvas.Children)
            {
                Title t = ue as Title;
                if (t != null)
                {
                    t.RefreshCollapseAdorner();
                    t.RefreshAddLowerLevelTitleAdorner();
                    t.RefreshAddSameLevelTitleAdorner();

                    if (t.IsOutBorderVisibled)
                    {
                        t.RefreshOutBorderVisibled();
                    }

                    t.RefreshDirection();
                }
            }

            RefreshFixedTitles();//必须先刷新FixedTitle的折叠状态。
            RefreshRelations();

            RefreshPageAdorner();

            //RefreshGlobleIndexes(this.RootTitle, 0);//只是这样会造成左右移动分支后GlobalsIndexes不准确。
            //要分开左右SubRoot

            if (this.RootTitle != null)
            {
                int startIndex = 0;
                this.RootTitle.GlobalIndex = startIndex;

                List<Title> subRoots = this.RootTitle.GetLowerLevelTitles();
                List<Title> leftSubRoots = new List<Title>();
                List<Title> rightSubRoots = new List<Title>();

                if (subRoots.Count > 0)
                {
                    foreach (var t in subRoots)
                    {
                        if (t.Direction == TitleDirection.Left)
                        {
                            leftSubRoots.Add(t);
                        }
                        else rightSubRoots.Add(t);
                    }

                    if (rightSubRoots.Count > 0)
                    {
                        foreach (var t in rightSubRoots)
                        {
                            t.GlobalIndex = ++startIndex;
                            startIndex = RefreshGlobleIndexes(t, startIndex);
                        }
                    }

                    if (leftSubRoots.Count > 0)
                    {
                        foreach (var t in leftSubRoots)
                        {
                            t.GlobalIndex = ++startIndex;
                            startIndex = RefreshGlobleIndexes(t, startIndex);
                        }
                    }
                }
            }

            this.RefreshMousePoint();
            this.RefreshMainSelectedTitle(this.MainSelectedTitle);
        }

        /// <summary>
        /// 只有列表才支持自动编号。
        /// </summary>
        public void RefreshAutoNumber()
        {
            if (this.View != ViewMode.HorizontalList && this.View != ViewMode.VerticalList) return;

            //只有列表才支持自动编号。

            foreach (UIElement ue in mainCanvas.Children)
            {
                Title title = ue as Title;
                if (title == null || title.IsFixed) continue;

                title.RefreshAutoNumberString();
            }
        }

        private void RefreshFixedTitles()
        {
            //所有源Title处于被折叠或源Title折叠下属的FixedTitle均隐藏

            foreach (UIElement ue in mainCanvas.Children)
            {
                FixedTitle ft = ue as FixedTitle;
                if (ft == null) continue;

                if (ft.HigherLevelTitleID != null && ft.HigherLevelTitleID.Length > 0)
                {
                    Title title = ft.GetHigherLvelTitle();
                    if (title != null)
                    {
                        if (title.BeCollapsed)
                        {
                            ft.BeCollapsed = true;
                        }
                        else
                        {
                            ft.BeCollapsed = false;
                        }
                    }
                    else
                    {
                        ft.BeCollapsed = true;
                    }
                }
            }
        }

        public void RefreshRelations()
        {
            foreach (UIElement ue in mainCanvas.Children)
            {
                Relation r = ue as Relation;
                if (r != null)
                {
                    r.RefreshLocation();

                    Title titleStart = r.StartTitle;
                    if (titleStart != null && titleStart.BeCollapsed)
                    {
                        r.Visibility = Visibility.Hidden;
                        continue;
                    }

                    Title titleEnd = r.EndTitle;
                    if (titleEnd != null && titleEnd.BeCollapsed)
                    {
                        r.Visibility = Visibility.Hidden;
                        continue;
                    }

                    r.Visibility = Visibility.Visible;
                }
            }
        }

        private void LocateTitlesByDefaultView(Title rootTitle, List<Title> subRootTitlesList)
        {
            //一级标题，要分左、右
            List<Title> leftSubRoots = new List<Title>();
            List<Title> rightSubRoots = new List<Title>();

            foreach (Title t in subRootTitlesList)
            {
                if (t.Direction == TitleDirection.Left)
                {
                    leftSubRoots.Add(t);
                }
                else
                {
                    //默认是右向的。
                    rightSubRoots.Add(t);
                }
            }

            //调用每个Title的RefreshNeedSize属性。计算出Canvas的大小以及每个Title所需要的空间。
            //在计算所需要的空间时，应注意：标题的“所需空间”是包括其所有下级所需空间的，因此是重叠的，不能简单相加。

            double maxWidthOfLeftSubTitles = 0;
            double maxWidthOfRightSubTitles = 0;
            double sumHeightOfLeftSubTitles = 0;
            double sumHeightOfRightSubTitles = 0;

            foreach (Title t in leftSubRoots)
            {
                t.RefreshNeedSize(false);//根标题永远不折叠其下的标题。
                if (maxWidthOfLeftSubTitles < t.NeedSize.Width)
                {
                    maxWidthOfLeftSubTitles = t.NeedSize.Width;
                }

                sumHeightOfLeftSubTitles += t.NeedSize.Height;
            }

            foreach (Title t in rightSubRoots)
            {
                t.RefreshNeedSize(false);//根标题永远不折叠其下的标题。
                if (maxWidthOfRightSubTitles < t.NeedSize.Width)
                {
                    maxWidthOfRightSubTitles = t.NeedSize.Width;
                }

                sumHeightOfRightSubTitles += t.NeedSize.Height;
            }

            //再加上根标题左右两侧的空白区
            double rootHeight = 0;
            double rootWidth = 0;

            double needHeight = Math.Max(sumHeightOfLeftSubTitles, sumHeightOfRightSubTitles);

            needHeight = Math.Max(rootTitle.ActualHeight, needHeight);

            //rootHeight = needHeight + Globals.blankPaddingOfMainCanvas * 2;//上下均留空白。
            rootHeight = needHeight + documentPadding.Top + documentPadding.Bottom;//上下均留空白。

            rootWidth =
                maxWidthOfLeftSubTitles +//左侧所有SubRoot的所需要宽度中最大的一个
                Globals.BlankAreaWidthMax +//左侧根与SubRoot留白区
                rootTitle.ActualWidth +//根标题自身所需的宽度
                Globals.BlankAreaWidthMax +//右侧根与SubRoot留白区
                maxWidthOfRightSubTitles +//右侧所有SubRoot的所需要宽度中最大的一个
                                          //Globals.blankPaddingOfMainCanvas * 2;//左右两端与上下两端一样，也留空白区。
                documentPadding.Left + documentPadding.Right;//左右两端与上下两端一样，也留空白区。

            rootTitle.SetNeedSize(rootWidth, rootHeight);//只是备用。

            mainCanvas.Width = rootWidth;
            mainCanvas.Height = rootHeight;

            //接下来计算根标题的位置。
            Point ptOfRootTitleTopLeft = new Point(0, 0);

            //根标题垂直居中，水平则取sumHeightOfLeftSubTitles+左侧留白区宽度。
            ptOfRootTitleTopLeft.X =
                maxWidthOfLeftSubTitles +//所有SubRoot中最占地方的一个的宽度
                documentPadding.Left +//画布左侧留白区
                Globals.BlankAreaWidthMax;//根标题与左SubRoot之间的留白区宽度。
            ptOfRootTitleTopLeft.Y = (rootHeight - rootTitle.ActualHeight) / 2;//垂直居中。

            Canvas.SetLeft(rootTitle, ptOfRootTitleTopLeft.X);
            Canvas.SetTop(rootTitle, ptOfRootTitleTopLeft.Y);

            rootTitle.SetLeftTop(ptOfRootTitleTopLeft.X, ptOfRootTitleTopLeft.Y);//仅备用。

            //分别排列左侧SubRoots和右侧SubRoots

            double leftSubRootBaseTop = (rootHeight - sumHeightOfLeftSubTitles) / 2;
            double leftSubRootBaseLeft = documentPadding.Left + maxWidthOfLeftSubTitles;

            //var centerX = rootTitle.LeftTop.X + rootTitle.ActualWidth / 2;
            //var centerY = rootTitle.LeftTop.Y + rootTitle.ActualHeight / 2;
            Rect rootTitleRect = new Rect(ptOfRootTitleTopLeft,
                new Point(ptOfRootTitleTopLeft.X + rootTitle.ActualWidth, ptOfRootTitleTopLeft.Y + rootTitle.ActualHeight));

            var arrayLeft = GetLeftOffsetArray(leftSubRoots.Count);

            for (int i = 0; i < leftSubRoots.Count; i++)
            {
                Title t = leftSubRoots[i];

                //传入的是标题右上角坐标。
                t.LocateAtLeft(new Point(leftSubRootBaseLeft - (Globals.BlankAreaWidthMax / 2) + arrayLeft[i], leftSubRootBaseTop));
                leftSubRootBaseTop += t.NeedSize.Height;
            }

            double rightSubRootBaseTop = (rootHeight - sumHeightOfRightSubTitles) / 2;
            double rightSubRootBaseLeft =
                documentPadding.Left +//画布左侧留白
                maxWidthOfLeftSubTitles +//所有左侧标题占据的宽度
                Globals.BlankAreaWidthMax +//根标题与左侧SubRoot之间的留白
                rootTitle.ActualWidth +//根标题自身的宽度
                Globals.BlankAreaWidthMax;//根标题与右侧SubRoot之间的留白

            var arrayRight = GetRightOffsetArray(rightSubRoots.Count);

            for (int i = 0; i < rightSubRoots.Count; i++)
            {
                Title t = rightSubRoots[i];

                //与左侧的标题不同：这里传入的是标题左上角坐标。
                t.LocateAtRight(new Point(rightSubRootBaseLeft + (Globals.BlankAreaWidthMax / 2) + arrayRight[i], rightSubRootBaseTop));
                rightSubRootBaseTop += t.NeedSize.Height;
            }

            //绘制自动关系线。

            //根面板与SubRoot之间的线单独绘制

            Point rootCenter = new Point(rootTitle.LeftTop.X + rootTitle.ActualWidth / 2,
                rootTitle.LeftTop.Y + rootTitle.ActualHeight / 2);

            for (int i = 0; i < leftSubRoots.Count; i++)
            {
                rootCenter = DrawOneLeftSubRootRelationLine(leftSubRoots, rootCenter, i);
            }

            for (int i = 0; i < rightSubRoots.Count; i++)
            {
                rootCenter = DrawOneRightSubRootRelationLine(rightSubRoots, rootCenter, i);
            }
        }

        private double[] GetLeftOffsetArray(int subRootTitlesCount)
        {
            double[] array;

            if (subRootTitlesCount <= 1)
            {
                array = new double[1];
                array[0] = 0.0;
                return array;
            }

            array = new double[subRootTitlesCount];

            var half = (int)(subRootTitlesCount / 2);
            var unitWidth = (Globals.BlankAreaWidthMax / subRootTitlesCount);
            var rate = 1.75;

            if (subRootTitlesCount % 2 == 0) // 偶数个
            {
                var preOffset = -unitWidth * rate;
                for (int i = half; i >= 1; i--)
                {
                    array[i - 1] = preOffset + unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }

                preOffset = -unitWidth * rate;
                for (int i = half + 1; i <= subRootTitlesCount; i++)
                {
                    array[i - 1] = preOffset + unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }
            }
            else  // 奇数个
            {
                var preOffset = 0.0;
                for (int i = half; i >= 1; i--)
                {
                    array[i - 1] = preOffset + unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }

                array[half + 1] = 0;  // 中间一个不用偏移。

                preOffset = 0.0;
                for (int i = half + 2; i <= subRootTitlesCount; i++)
                {
                    array[i - 1] = preOffset + unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }
            }

            return array;
        }

        private double[] GetRightOffsetArray(int subRootTitlesCount)
        {
            double[] array;

            if (subRootTitlesCount <= 1)
            {
                array = new double[1];
                array[0] = 0.0;
                return array;
            }

            array = new double[subRootTitlesCount];

            var half = (int)(subRootTitlesCount / 2);
            var unitWidth = (Globals.BlankAreaWidthMax / subRootTitlesCount);
            var rate = 1.75;

            if (subRootTitlesCount % 2 == 0) // 偶数个
            {
                var preOffset = unitWidth * rate;
                for (int i = half; i >= 1; i--)
                {
                    array[i - 1] = preOffset - unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }

                preOffset = unitWidth * rate;
                for (int i = half + 1; i <= subRootTitlesCount; i++)
                {
                    array[i - 1] = preOffset - unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }
            }
            else  // 奇数个
            {
                var preOffset = 0.0;
                for (int i = half; i >= 1; i--)
                {
                    array[i - 1] = preOffset - unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }

                array[half + 1] = 0.0;   // 中间一个不用偏移。

                preOffset = 0.0;
                for (int i = half + 2; i <= subRootTitlesCount; i++)
                {
                    array[i - 1] = preOffset - unitWidth * rate;
                    preOffset = array[i - 1];
                    continue;
                }
            }

            return array;
        }

        private Thickness documentPadding = new Thickness(Globals.BlankAreaWidthMax * 2);
        /// <summary>
        /// 文档四周的空白区宽度。用以放置一些备注之类的部件。
        /// 2012年5月24日
        /// </summary>
        public Thickness DocumentPadding
        {
            get { return documentPadding; }
            set
            {
                documentPadding = value;
                if (this.mainXmlDocument != null && this.mainXmlDocument.DocumentElement != null)
                {
                    this.mainXmlDocument.DocumentElement.SetAttribute("DocumentPadding", documentPadding.ToString());
                }

                this.RefreshTitleAndLinesLocation();
            }
        }

        private void LocateTitlesByHorizontalListView(Title rootTitle, List<Title> subRootTitlesList)
        {
            if (rootTitle == null || subRootTitlesList == null || subRootTitlesList.Count <= 0) return;

            double maxHeightOfSubRoots = 0;
            double sumWidthOfSubRoots = 0;

            #region 重排下序，先右后左。
            List<Title> leftSubRoots = new List<Title>();
            List<Title> rightSubRoots = new List<Title>();
            foreach (Title t in subRootTitlesList)
            {
                if (t.Direction == TitleDirection.Left)
                {
                    leftSubRoots.Add(t);
                }
                else
                {
                    //默认是右向的。
                    rightSubRoots.Add(t);
                }
            }
            subRootTitlesList.Clear();
            subRootTitlesList.AddRange(rightSubRoots);
            subRootTitlesList.AddRange(leftSubRoots);
            #endregion

            foreach (Title t in subRootTitlesList)
            {
                t.RefreshNeedSize(false);//根标题永远不折叠其下的标题。
                if (t.NeedSize.Height > maxHeightOfSubRoots)
                {
                    maxHeightOfSubRoots = t.NeedSize.Height;
                }
                sumWidthOfSubRoots += t.NeedSize.Width;
            }

            double rootHeight = 0; double rootWidth = 0;
            var blankAreaWidth = Globals.BlankAreaWidth;

            double needHeight = rootTitle.ActualHeight
                + blankAreaWidth//拿来作为根标题与一级标题[SubRoot]之间的空白区的高度。
                + documentPadding.Top + documentPadding.Bottom //这是整个文档顶、底部的空白区。
                + maxHeightOfSubRoots
                + blankAreaWidth;//用以绘制底部双横线所需要

            double needWidth = sumWidthOfSubRoots + documentPadding.Left + documentPadding.Right +
                +(subRootTitlesList.Count + 1) * blankAreaWidth;//其实是-1+2

            rootHeight = needHeight; rootWidth = Math.Max(rootTitle.ActualWidth, needWidth);

            rootTitle.SetNeedSize(rootWidth, rootHeight);

            mainCanvas.Width = needWidth;
            mainCanvas.Height = needHeight;

            rootTitle.SetLeftTop((mainCanvas.Width - rootTitle.ActualWidth) / 2, documentPadding.Top);

            Canvas.SetLeft(rootTitle, rootTitle.LeftTop.X);
            Canvas.SetTop(rootTitle, rootTitle.LeftTop.Y);

            //基本上是纵向：第１层，文档顶空白区；第二层，根标题；第３层，根标题与一级标题[SubRoot]间的空白区；
            //第４层，一级标题（所有一级标题都在第四层由左至右横向排列）；第５层，文档底部空白区。
            double subRootBaseTop = documentPadding.Top
                + rootTitle.ActualHeight + blankAreaWidth;
            double subRootBaseLeft = documentPadding.Left + blankAreaWidth;

            for (int i = 0; i < subRootTitlesList.Count; i++)
            {
                Title t = subRootTitlesList[i];

                t.LocateAsListViewItem(new Point(subRootBaseLeft, subRootBaseTop));
                subRootBaseLeft += t.NeedSize.Width + blankAreaWidth;
            }

            //绘制关系线。

            //先递归绘制一级标题到下级、下下级等等的关系线。
            for (int i = 0; i < subRootTitlesList.Count; i++)
            {
                DrawOneSubRootListRelationLine(subRootTitlesList, i, subRootBaseTop);
            }

            //再画根标题到一级标题的关系线——两条横线好了！！！
            Path path = new Path();
            path.Fill = path.Stroke = rootTitle.TitleForeground;
            path.StrokeLineJoin = PenLineJoin.Miter;
            path.StrokeThickness = 1;
            path.SnapsToDevicePixels = true;

            RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);

            mainCanvas.Children.Add(path);
            Canvas.SetZIndex(path, 0);

            PathGeometry pg = new PathGeometry();
            path.Data = pg;

            Point startPoint = FormatPoint(new Point(documentPadding.Left, subRootBaseTop - 8));
            PathFigure pf = new PathFigure() { StartPoint = startPoint, IsClosed = false, };
            pg.Figures.Add(pf);

            LineSegment ls1 = new LineSegment()
            {
                Point = new Point(mainCanvas.Width - documentPadding.Right, startPoint.Y)
            };
            pf.Segments.Add(ls1);

            PathFigure pf2 = new PathFigure() { StartPoint = FormatPoint(new Point(startPoint.X, startPoint.Y + 8)), IsClosed = false };
            pg.Figures.Add(pf2);

            LineSegment ls2 = new LineSegment() { Point = FormatPoint(new Point(ls1.Point.X, ls1.Point.Y + 8)) };
            pf2.Segments.Add(ls2);

            PathFigure pf3 = new PathFigure()
            {
                StartPoint = FormatPoint(new Point(mainCanvas.Width / 2,
                    rootTitle.LeftTop.Y + rootTitle.ActualHeight - 10)),
                IsClosed = false
            };
            pg.Figures.Add(pf3);

            LineSegment ls3 = new LineSegment() { Point = new Point(pf3.StartPoint.X, ls1.Point.Y) };
            pf3.Segments.Add(ls3);

            //底端的两条横线。
            PathFigure pf5 = new PathFigure()
            {
                StartPoint = FormatPoint(new Point(pf2.StartPoint.X, mainCanvas.Height - documentPadding.Bottom - blankAreaWidth / 2)),
                IsClosed = false
            };
            pg.Figures.Add(pf5);

            LineSegment ls5 = new LineSegment()
            {
                Point = FormatPoint(new Point(ls2.Point.X, pf5.StartPoint.Y))
            };
            pf5.Segments.Add(ls5);

            PathFigure pf6 = new PathFigure()
            {
                StartPoint = FormatPoint(new Point(pf5.StartPoint.X, pf5.StartPoint.Y + 8)),
                IsClosed = false
            };
            pg.Figures.Add(pf6);

            LineSegment ls6 = new LineSegment()
            {
                Point = FormatPoint(new Point(ls5.Point.X, ls5.Point.Y + 8))
            };
            pf6.Segments.Add(ls6);
        }

        private void LocateTitlesByVerticalListView(Title rootTitle, List<Title> subRootTitlesList)
        {
            if (rootTitle == null || subRootTitlesList == null || subRootTitlesList.Count <= 0) return;

            double maxWidthOfSubRoots = 0;
            double sumHeightOfSubRoots = 0;

            #region 重排下序，先右后左。
            List<Title> leftSubRoots = new List<Title>();
            List<Title> rightSubRoots = new List<Title>();
            foreach (Title t in subRootTitlesList)
            {
                if (t.Direction == TitleDirection.Left)
                {
                    leftSubRoots.Add(t);
                }
                else
                {
                    //默认是右向的。
                    rightSubRoots.Add(t);
                }
            }
            subRootTitlesList.Clear();
            subRootTitlesList.AddRange(rightSubRoots);
            subRootTitlesList.AddRange(leftSubRoots);
            #endregion

            foreach (Title t in subRootTitlesList)
            {
                t.RefreshNeedSize(false);//根标题永远不折叠其下的标题。
                if (t.NeedSize.Width > maxWidthOfSubRoots)
                {
                    maxWidthOfSubRoots = t.NeedSize.Width;
                }
                sumHeightOfSubRoots += t.NeedSize.Height;
            }

            double rootHeight = 0; double rootWidth = 0;
            var blankAreaWidth = Globals.BlankAreaWidth;

            double needHeight = rootTitle.ActualHeight
                + blankAreaWidth//拿来作为根标题与一级标题[SubRoot]之间的空白区的高度。
                + documentPadding.Top + documentPadding.Bottom//这是整个文档顶、底部的空白区。
                + sumHeightOfSubRoots
                + blankAreaWidth;//用于绘制底部双横线

            double needWidth = Math.Max(maxWidthOfSubRoots, rootTitle.ActualWidth)
                + documentPadding.Left + documentPadding.Right
                + blankAreaWidth;//用于一级标题（SubRootTitles）左缩进

            rootHeight = needHeight; rootWidth = needWidth;//这和横向列表不同。

            rootTitle.SetNeedSize(rootWidth, rootHeight);

            mainCanvas.Width = needWidth;
            mainCanvas.Height = needHeight;

            rootTitle.SetLeftTop(documentPadding.Left, documentPadding.Top);

            Canvas.SetLeft(rootTitle, documentPadding.Left);
            Canvas.SetTop(rootTitle, documentPadding.Top);

            //基本上是纵向：第１层，文档顶空白区；第二层，根标题；第３层，依索引纵向排列的一级标题及其下级；
            //第４层文档底部空白区。
            double subRootBaseTop = documentPadding.Top + rootTitle.ActualHeight + blankAreaWidth;
            double subRootBaseLeft = documentPadding.Left + blankAreaWidth;

            for (int i = 0; i < subRootTitlesList.Count; i++)
            {
                Title t = subRootTitlesList[i];

                t.LocateAsListViewItem(FormatPoint(new Point(subRootBaseLeft, subRootBaseTop)));
                subRootBaseTop += t.NeedSize.Height;//这里的一级标题[SubRoot]都是纵向依次排列。
            }

            //绘制关系线。

            double baseTopOfFstSubRootTitle = documentPadding.Top + rootTitle.ActualHeight + 20;

            //先递归绘制一级标题到下级、下下级等等的关系线。
            for (int i = 0; i < subRootTitlesList.Count; i++)
            {
                DrawOneSubRootListRelationLine(subRootTitlesList, i, baseTopOfFstSubRootTitle);
            }

            //再画根标题到一级标题的关系线——2条横线好了！！！
            Path path = new Path();
            path.Fill = path.Stroke = rootTitle.TitleForeground;
            path.StrokeLineJoin = PenLineJoin.Miter;
            path.StrokeThickness = 1;
            path.SnapsToDevicePixels = true;

            RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);

            mainCanvas.Children.Add(path);
            Canvas.SetZIndex(path, 0);

            PathGeometry pg = new PathGeometry();
            path.Data = pg;

            Point startPoint = FormatPoint(new Point(documentPadding.Left, baseTopOfFstSubRootTitle - 10));
            PathFigure pf = new PathFigure() { StartPoint = startPoint, IsClosed = false };
            pg.Figures.Add(pf);

            LineSegment ls1 = new LineSegment()
            {
                Point = FormatPoint(new Point(mainCanvas.Width - documentPadding.Right, startPoint.Y))
            };
            pf.Segments.Add(ls1);

            PathFigure pf2 = new PathFigure() { StartPoint = FormatPoint(new Point(startPoint.X, startPoint.Y + 8)), IsClosed = false };
            pg.Figures.Add(pf2);

            LineSegment ls2 = new LineSegment() { Point = FormatPoint(new Point(ls1.Point.X, ls1.Point.Y + 8)) };
            pf2.Segments.Add(ls2);

            PathFigure pf3 = new PathFigure()
            {
                StartPoint = FormatPoint(new Point(subRootBaseLeft - blankAreaWidth / 2, startPoint.Y + 8)),
                IsClosed = false
            };
            pg.Figures.Add(pf3);

            LineSegment ls3 = new LineSegment()
            {
                Point = FormatPoint(new Point(pf3.StartPoint.X,
                    mainCanvas.Height - documentPadding.Bottom - blankAreaWidth / 2))
            };
            pf3.Segments.Add(ls3);

            //PathFigure pf4 = new PathFigure()
            //{
            //    StartPoint = new Point(subRootBaseLeft - Globals.blankAreaWidthMax, startPoint.Y),
            //    IsClosed = false
            //};
            //pg.Figures.Add(pf4);

            //LineSegment ls4 = new LineSegment()
            //{
            //    Point = new Point(subRootBaseLeft - Globals.blankAreaWidthMax,
            //        mainCanvas.Height - Globals.blankPaddingOfMainCanvas)
            //};
            //pf4.Segments.Add(ls4);

            PathFigure pf5 = new PathFigure()
            {
                StartPoint = FormatPoint(new Point(pf2.StartPoint.X, ls3.Point.Y)),
                IsClosed = false
            };
            pg.Figures.Add(pf5);

            LineSegment ls5 = new LineSegment()
            {
                Point = FormatPoint(new Point(ls2.Point.X, pf5.StartPoint.Y))
            };
            pf5.Segments.Add(ls5);

            PathFigure pf6 = new PathFigure()
            {
                StartPoint = FormatPoint(new Point(pf5.StartPoint.X, pf5.StartPoint.Y + 8)),
                IsClosed = false
            };
            pg.Figures.Add(pf6);

            LineSegment ls6 = new LineSegment()
            {
                Point = FormatPoint(new Point(ls5.Point.X, ls5.Point.Y + 8))
            };
            pf6.Segments.Add(ls6);
        }

        public Point FormatPoint(Point src)
        {
            src.X = (int)src.X;
            src.Y = (int)src.Y;
            return src;
        }

        /// <summary>
        /// DrawOneSubRootHorizontalListViewItemRelationLine。
        /// </summary>
        /// <param name="rightSubRoots"></param>
        /// <param name="rootCenter"></param>
        /// <param name="i"></param>
        private void DrawOneSubRootListRelationLine(List<Title> subRootTitlesList, int i, double horizontalLineTop)
        {
            Title t = subRootTitlesList[i];

            Brush brush = t.RelationLineStroke;
            XmlAttribute attrAutoRelationLineStroke = t.XmlData.GetAttribute("RelationLineStroke");
            if (attrAutoRelationLineStroke == null || attrAutoRelationLineStroke.Value.Length <= 0)
            {
                t.RelationLineStroke = brush;//这会写后台Xml数据。
            }

            Path path = new Path();
            t.AutoRelationPath = path;

            path.Fill = path.Stroke = brush;
            path.StrokeLineJoin = PenLineJoin.Round;
            path.StrokeThickness = 1;

            RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);

            mainCanvas.Children.Add(path);
            Canvas.SetZIndex(path, 0);

            PathGeometry pg = new PathGeometry();
            path.Data = pg;

            t.DrawListRelationLine(pg);
            var blankAreaWidth = Globals.BlankAreaWidth;

            Point startPt = FormatPoint(new Point(Canvas.GetLeft(t) - blankAreaWidth / 2, Canvas.GetTop(t) + t.ActualHeight));

            PathFigure pf = new PathFigure() { StartPoint = startPt, IsClosed = false };
            pg.Figures.Add(pf);

            LineSegment ls = new LineSegment() { Point = FormatPoint(new Point(startPt.X + t.ActualWidth + blankAreaWidth / 2, startPt.Y)) };
            pf.Segments.Add(ls);

            if (view == ViewMode.HorizontalList || t.HigherLevelTitleID != RootTitle.ID)//纵向列表不需要一级标题画竖线。
            {
                PathFigure pfVertical = new PathFigure() { StartPoint = startPt, IsClosed = false };
                pg.Figures.Add(pfVertical);

                LineSegment lsVertical = new LineSegment() { Point = FormatPoint(new Point(startPt.X, horizontalLineTop)) };
                pfVertical.Segments.Add(lsVertical);
            }
        }

        private Point DrawOneRightSubRootRelationLine(List<Title> rightSubRoots, Point rootCenter, int i)
        {
            Title t = rightSubRoots[i];

            Brush brush = t.RelationLineStroke;
            XmlAttribute attrAutoRelationLineStroke = t.XmlData.GetAttribute("RelationLineStroke");
            if (attrAutoRelationLineStroke == null || attrAutoRelationLineStroke.Value.Length <= 0)
            {
                t.RelationLineStroke = brush;//这会写后台Xml数据。
            }

            Path path = new Path();
            t.AutoRelationPath = path;

            path.Fill = path.Stroke = brush;
            path.StrokeLineJoin = PenLineJoin.Bevel;
            path.StrokeThickness = 1.8;
            mainCanvas.Children.Add(path);
            Canvas.SetZIndex(path, 0);

            PathGeometry pg = new PathGeometry();
            path.Data = pg;

            PathFigure pf = new PathFigure() { IsClosed = true };
            pg.Figures.Add(pf);

            Point ptCtrl1 = new Point(rootCenter.X + Globals.SubRootTitleCtrlOffcet, (int)(t.LeftTop.Y + t.ActualHeight));
            Point ptEnd1 = new Point(t.LeftTop.X/* - Globals.blankAreaWidth*/, ptCtrl1.Y);// - 1.8);

            Point rootCenterLeft = new Point(rootCenter.X - 8, rootCenter.Y);
            Point rootCenterRight = new Point(rootCenter.X + 8, rootCenter.Y);

            pf.StartPoint = new Point(rootCenterLeft.X + 16, rootCenterLeft.Y);

            QuadraticBezierSegment qbs1 = new QuadraticBezierSegment(
                new Point(ptCtrl1.X - 8, ptCtrl1.Y), ptEnd1, true);

            pf.Segments.Add(qbs1);

            Point ptCtrl2 = new Point(ptEnd1.X + Globals.BlankAreaWidthMax / 4, ptEnd1.Y);
            Point ptCtrl3 = new Point(ptCtrl2.X, ptCtrl1.Y);
            Point ptEnd2 = new Point(t.LeftTop.X, ptCtrl3.Y);

            //BezierSegment bs1 = new BezierSegment(ptCtrl2, ptCtrl3, ptEnd2, true);
            //BezierSegment bs2 = new BezierSegment(ptCtrl3, ptCtrl2, ptEnd1, true);

            //pf.Segments.Add(bs1);
            //pf.Segments.Add(bs2);

            QuadraticBezierSegment qbs2 = new QuadraticBezierSegment(
                new Point(ptCtrl1.X + 8, ptCtrl1.Y),
                new Point(rootCenterRight.X + 16, rootCenterRight.Y),
                true);

            pf.Segments.Add(qbs2);

            //递归绘制下层线。每个SubRoot拥有一个Path对象。
            //Path pathLower = new Path();
            //t.LowerAutoRelationPath = pathLower;

            //pathLower.Stroke = brush;
            //pathLower.StrokeThickness = 2.4;
            //mainCanvas.Children.Add(pathLower);

            //PathGeometry pathGeometry = new PathGeometry();
            //pathLower.Data = pathGeometry;

            //t.DrawRightLine(pathGeometry.Figures, new Point(ptEnd2.X - 2, ptEnd2.Y));//防止断线
            t.DrawRightLine(pg.Figures, new Point(ptEnd2.X - 2, ptEnd2.Y));
            return rootCenter;
        }

        private Point DrawOneLeftSubRootRelationLine(List<Title> leftSubRoots, Point rootCenter, int i)
        {
            Title t = leftSubRoots[i];

            Brush brush = t.RelationLineStroke;
            XmlAttribute attrAutoRelationLineStroke = t.XmlData.GetAttribute("RelationLineStroke");
            if (attrAutoRelationLineStroke == null || attrAutoRelationLineStroke.Value.Length <= 0)
            {
                t.RelationLineStroke = brush;//这会写后台Xml数据。
            }

            Path path = new Path();
            t.AutoRelationPath = path;
            path.Fill = path.Stroke = brush;
            path.SnapsToDevicePixels = true;
            path.StrokeLineJoin = PenLineJoin.Round;
            //path.StrokeDashArray = new DoubleCollection() { 2, 2, };//虚线效果差。
            path.StrokeThickness = 1.8;
            mainCanvas.Children.Add(path);
            Canvas.SetZIndex(path, 0);

            PathGeometry pg = new PathGeometry();
            path.Data = pg;

            PathFigure pf = new PathFigure() { IsClosed = false };
            pg.Figures.Add(pf);

            //Point ptCtrl1 = new Point(rootCenter.X, (int)(t.LeftTop.Y + t.ActualHeight / 2));
            Point ptCtrl1 = new Point(rootCenter.X - Globals.SubRootTitleCtrlOffcet, (int)(t.LeftTop.Y + t.ActualHeight));
            Point ptEnd1 = new Point(t.LeftTop.X + t.ActualWidth /*+ Globals.blankAreaWidth*/, ptCtrl1.Y);// - 1.8);

            Point rootCenterLeft = new Point(rootCenter.X - 8, (int)(rootCenter.Y));
            Point rootCenterRight = new Point(rootCenter.X + 8, rootCenterLeft.Y);

            pf.StartPoint = new Point(rootCenterLeft.X - 16, rootCenterLeft.Y);

            QuadraticBezierSegment qbs1 = new QuadraticBezierSegment(
                new Point(ptCtrl1.X - 8, ptCtrl1.Y), ptEnd1, true);

            pf.Segments.Add(qbs1);

            Point ptCtrl2 = new Point(ptEnd1.X - Globals.BlankAreaWidthMax / 4, ptEnd1.Y);
            Point ptCtrl3 = new Point(ptCtrl2.X, ptCtrl1.Y);//t.LeftTop.Y + t.ActualHeight - 2);
            Point ptEnd2 = new Point(t.LeftTop.X + t.ActualWidth, ptCtrl3.Y);

            //BezierSegment bs1 = new BezierSegment(ptCtrl2, ptCtrl3, ptEnd2, true);
            //BezierSegment bs2 = new BezierSegment(ptCtrl3, ptCtrl2, ptEnd1, true);

            //pf.Segments.Add(bs1);
            //pf.Segments.Add(bs2);

            QuadraticBezierSegment qbs2 = new QuadraticBezierSegment(
                new Point(ptCtrl1.X + 8, ptCtrl1.Y),
                new Point(rootCenterRight.X - 16, rootCenterRight.Y),
                true);

            pf.Segments.Add(qbs2);

            //递归绘制下层线。每个SubRoot拥有一个Path对象。
            //Path pathLower = new Path();
            //t.LowerAutoRelationPath = pathLower;

            //t.LowerAutoRelationPath = path;

            //pathLower.Stroke = brush;
            //pathLower.StrokeThickness = 2.4;
            //mainCanvas.Children.Add(pathLower);

            //PathGeometry pathLowerGeometry = new PathGeometry();
            //pathLower.Data = pathLowerGeometry;

            //t.DrawLeftLine(pathLowerGeometry.Figures, new Point(ptEnd2.X + 2, ptEnd2.Y));//防止断线

            t.DrawLeftLine(pg.Figures, new Point(ptEnd2.X + 2, ptEnd2.Y));//防止断线
            return rootCenter;
        }

        private void tbScaleNumber_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                sliderOfCanvasScale.Value = 0;
            }
        }

        /// <summary>
        /// 清除所有被选中的标题。
        /// </summary>
        public void ClearAllFromSelectedTitleList()
        {
            foreach (Title t in SelectedTitlesList)
            {
                if (t.IsMainSelectedTitle) t.IsMainSelectedTitle = false;

                t.IsSelected = false;
            }

            selectedTitlesList.Clear();

            titleDraggingHandler.Visibility = Visibility.Hidden;

            tbkTitleGlobalIndex.Text = "";
        }

        /// <summary>
        /// 这两个点用于截图、框选标题。
        /// </summary>
        private Point selPointA = new Point(-1, -1);

        private Point selPointB = new Point(-1, -1);

        private Rect SnaptShortRect
        {
            get
            {
                return new Rect(selPointA, selPointB);
            }
        }

        private Point SnaptLeftTop
        {
            get { return SnaptShortRect.TopLeft; }
        }

        private void mainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (isPrepareInsertCommentOrLinkTitle) isPrepareInsertCommentOrLinkTitle = false;
            if (isPrepareInsertPictureBox) isPrepareInsertPictureBox = false;
            if (isPrepareInsertFixedTitle) isPrepareInsertFixedTitle = false;

            if (isPrepareSnapshot)
            {
                selPointA = e.GetPosition(mainCanvas);
                return;
            }

            //清除所有被选中的标题。
            if (SelectedTitlesList.Count > 0)
            {
                ClearAllFromSelectedTitleList();
            }

            //否则会造成无法顺利调整曲率。
            if (SelectedRelation != null)
            {
                SelectedRelation = null;
            }

            if (SelectedComment != null)
            {
                SelectedComment = null;
            }

            if (SelectedTitleImage != null)
            {
                SelectedTitleImage = null;
            }

            if (SelectedPictureBox != null)
            {
                SelectedPictureBox = null;
            }

            e.Handled = true;
        }

        #region 关于撤销与重做的相关功能

        private ModifingsList modifingsList;

        public ModifingsList ModifingsList
        {
            get { return modifingsList; }
        }

        /// <summary>
        /// 这个方法仅仅只是封装了ModifingItem的Do()方法——便于调用而已。
        /// 没有添加任何其它代码。
        /// </summary>
        /// <param name="modifingItem"></param>
        public void Do(ModifingItem modifingItem)
        {
            if (modifingsList != null)
            {
                modifingsList.Do(modifingItem);
            }
        }

        /// <summary>
        /// 这个方法仅仅只是封装了ModifingItem的UnDo()方法——便于调用而已。
        /// 没有添加任何其它代码。
        /// </summary>
        public void Undo()
        {
            if (modifingsList != null)
            {
                modifingsList.Undo();
            }
        }

        /// <summary>
        /// 这个方法仅仅只是封装了ModifingItem的ReDo()方法——便于调用而已。
        /// 没有添加任何其它代码。
        /// </summary>
        public void Redo()
        {
            if (modifingsList != null)
            {
                modifingsList.Redo();
            }
        }

        #endregion

        private void miAbout_Click(object sender, RoutedEventArgs e)
        {
            Dialogs.AboutBox aboutBox = new Dialogs.AboutBox(this);
            aboutBox.ShowDialog();
        }

        private void sliderBorderWidth_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (Globals.MainWindow.SelectedTitlesList.Count <= 0)
            {
                MessageBox.Show("没选中任何标题。此滑块条用以设置标题的边框宽度！", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
            }

            Commands.TitleBorderWidthCommand.SetTitleBorderWidth((int)sliderBorderWidth.Value);
        }

        private void Window_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                Array dragDataArray = ((System.Array)e.Data.GetData(DataFormats.FileDrop));
                List<string> filenames = new List<string>();

                foreach (string s in dragDataArray)
                {
                    filenames.Add(s);
                }

                if (filenames.Count > 0)
                {
                    if (IsModified)
                    {
                        MessageBoxResult mr = MessageBox.Show("当前文档已被修改，要保存吗？", Globals.AppName,
                            MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Yes);

                        switch (mr)
                        {
                            case MessageBoxResult.Yes:
                                {
                                    if (Commands.SaveDocumentCommand.SaveDocument())
                                    {
                                        OpenDocument(filenames);
                                    }
                                    else return;

                                    break;
                                }
                            case MessageBoxResult.No:
                                {
                                    OpenDocument(filenames);
                                    break;
                                }
                            default:
                                {
                                    return;//其余都作Cancel看待。
                                }
                        }
                    }
                    else
                    {
                        OpenDocument(filenames);
                    }
                }

                e.Handled = true;
            }
        }

        private static void OpenDocument(List<string> filenames)
        {
            if (filenames[0].EndsWith(".lunarmind"))
            {
                Commands.OpenDocumentCommand.ReadDocumentFromDiskFile(filenames[0]);
                Globals.MainWindow.TryLoadWorkspace(true);

                RecentFileItem.WriteRecentFilesToXmlFile(filenames[0]);
                RecentFileItem.ReadRecentFilesFromXmlFile();
            }
            else if (filenames[0].EndsWith(".png"))
            {
                Commands.OpenDocumentCommand.ReadOutportedPNGImageFile(filenames[0]);
                Globals.MainWindow.TryLoadWorkspace(true);

                RecentFileItem.WriteRecentFilesToXmlFile(filenames[0]);
                RecentFileItem.ReadRecentFilesFromXmlFile();
            }
            else
            {
                MessageBox.Show("不支持这种类型的文件！请检查文件后缀名，并确定此文件是由《" + Globals.AppName + "》生成的。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
        }

        private void miExit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            //总是记录，避免多重代码。
            Properties.Settings.Default.StartWindowLocationSetting = this.RestoreBounds;
            Properties.Settings.Default.Save();

            //记录当前缩放比例
            ConfigManager.Set("Zoom", sliderOfCanvasScale.Value.ToString());

            if (this.CloseDocument() == false) e.Cancel = true;
        }

        internal bool CloseDocument()
        {
            if (IsModified)
            {
                MessageBoxResult r = MessageBox.Show("当前文档已被修改。要保存吗？",
                    Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning,
                    MessageBoxResult.Yes);

                switch (r)
                {
                    case MessageBoxResult.Yes:
                        {
                            if (Commands.SaveDocumentCommand.SaveDocument() != true)
                            {
                                return false;
                            }
                            else return true;
                        }
                    case MessageBoxResult.No:
                        {
                            return true;//直接关闭。
                        }
                    default:
                        {
                            return false;
                        }
                }
            }
            else return true;
        }

        private SimpleHelpWindow helpWindow;

        private void miHelp_Click(object sender, RoutedEventArgs e)
        {
            //MessageBox.Show("选定标题后，\r\n==========\r\n" + LunarMind.Title.TextBlockToolTip, "Lunar Mind", MessageBoxButton.OK, MessageBoxImage.Information);

            if (helpWindow == null)
            {
                helpWindow = new SimpleHelpWindow() { Owner = this, WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner, };
            }

            helpWindow.Show();
        }

        private void ppTitleForeground_Opened(object sender, EventArgs e)
        {
            //isPrepareInsertFixedTitle = false;//这两个不必要，因为需要按Ctrl或Shift键。
            //isPrepareInsertPictureBox = false;
            isPrepareInsertCommentOrLinkTitle = false;

            ppTitleBackground.IsOpen = false;
            ppTitleBorderColor.IsOpen = false;
            ppTitleRelationLineStroke.IsOpen = false;
        }

        private void ppTitleBackground_Opened(object sender, EventArgs e)
        {
            //isPrepareInsertFixedTitle = false;//这两个不必要，因为需要按Ctrl或Shift键。
            //isPrepareInsertPictureBox = false;
            isPrepareInsertCommentOrLinkTitle = false;

            ppTitleBorderColor.IsOpen = false;
            ppTitleForeground.IsOpen = false;
            ppTitleRelationLineStroke.IsOpen = false;
        }

        private void ppTitleBorderColor_Opened(object sender, EventArgs e)
        {
            //isPrepareInsertFixedTitle = false;//这两个不必要，因为需要按Ctrl或Shift键。
            //isPrepareInsertPictureBox = false;
            isPrepareInsertCommentOrLinkTitle = false;

            ppTitleForeground.IsOpen = false;
            ppTitleBackground.IsOpen = false;
            ppTitleRelationLineStroke.IsOpen = false;
        }

        private void bpForegound_BrushPicked(object sender, LunarTools.BrushPickedEventArgs e)
        {
            if (Globals.MainWindow.SelectedRelation != null)
            {
                Relation r = Globals.MainWindow.SelectedRelation;

                ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线前景色" };

                Action actRelationForeground = new Action(r.ID, -1, r.GetType().Name, "TextForeground",
                    ColorPicker.GetBrushName(r.TextForeground), e.EnName);

                r.TextForeground = e.PickedBrush;

                mi.Add(actRelationForeground);

                Do(mi);
                IsModified = true;
                ppTitleForeground.IsOpen = false;
                return;
            }

            //设置comment前景色。
            if (Globals.MainWindow.selectedComment != null)
            {
                Comment c = Globals.MainWindow.selectedComment;

                ModifingItem mi = new ModifingItem() { ModifingName = "设置备注文本块前景色" };

                Action actCommentForeground = new Action(c.ID, -1, c.GetType().Name, "TextForeground",
                    ColorPicker.GetBrushName(c.TextForeground), e.EnName);

                c.TextForeground = e.PickedBrush;

                mi.Add(actCommentForeground);

                Do(mi);
                IsModified = true;
                ppTitleForeground.IsOpen = false;
                return;
            }


            //设置标题前景色。
            if (Globals.SelectedTitlesList.Count > 0)
            {
                ModifingItem mi = new ModifingItem()
                {
                    IsNeedRefreshTitleAndLinesLocation = false,
                    ModifingName = "设置标题前景色"
                };

                foreach (Title t in Globals.SelectedTitlesList)
                {
                    Action actForeground = new Action(t.ID, -1, t.GetType().Name,
                        "TitleForeground", ColorPicker.GetBrushName(t.TitleForeground),
                        e.EnName);

                    t.TitleForeground = e.PickedBrush;

                    mi.Add(actForeground);
                }

                Do(mi);
                IsModified = true;
            }

            ppTitleForeground.IsOpen = false;
        }

        private void bpForegound_CloseButtonClicked(object sender, MouseButtonEventArgs e)
        {
            ppTitleForeground.IsOpen = false;//关闭前景色PopUp
        }

        private void bpBackground_BrushPicked(object sender, LunarTools.BrushPickedEventArgs e)
        {
            if (SelectedRelation != null)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线备注文本背景色" };

                Action actTextBackground = new Action(SelectedRelation.ID, -1, SelectedRelation.GetType().Name,
                    "TextBackground", ColorPicker.GetBrushName(SelectedRelation.TextBackground), e.EnName);

                SelectedRelation.TextBackground = e.PickedBrush;

                mi.Add(actTextBackground);

                Do(mi);
                IsModified = true;

                ppTitleBackground.IsOpen = false;
                return;
            }

            //设置comment背景色。
            if (SelectedComment != null)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "设置备注文本块背景色" };

                Action actCommentTextBackground = new Action(SelectedComment.ID, -1, SelectedComment.GetType().Name,
                    "TextBackground", ColorPicker.GetBrushName(SelectedComment.TextBackground), e.EnName);

                SelectedComment.TextBackground = e.PickedBrush;

                mi.Add(actCommentTextBackground);

                Do(mi);
                IsModified = true;

                ppTitleBackground.IsOpen = false;
                return;
            }

            //设置标题背景色。
            if (Globals.SelectedTitlesList.Count > 0)
            {
                ModifingItem mi = new ModifingItem()
                {
                    IsNeedRefreshTitleAndLinesLocation = false,
                    ModifingName = "设置标题背景色"
                };

                foreach (Title t in Globals.SelectedTitlesList)
                {
                    Action actBackground = new Action(t.ID, -1, t.GetType().Name,
                        "TitleBackground", ColorPicker.GetBrushName(t.TitleBackground),
                        e.EnName);

                    t.TitleBackground = e.PickedBrush;

                    mi.Add(actBackground);
                }

                Do(mi);
                IsModified = true;
            }

            ppTitleBackground.IsOpen = false;
        }

        private void bpBackground_CloseButtonClicked(object sender, MouseButtonEventArgs e)
        {
            ppTitleBackground.IsOpen = false;//关闭背景色PopUp
        }

        private void bpBorderColor_BrushPicked(object sender, LunarTools.BrushPickedEventArgs e)
        {
            if (SelectedComment != null)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "设置备注文本块边框色" };

                Action actCommentBorderColor = new Action(SelectedComment.ID, -1, SelectedComment.GetType().Name,
                    "BorderColor", ColorPicker.GetBrushName(SelectedComment.BorderColor), e.EnName);

                SelectedComment.BorderColor = e.PickedBrush;

                mi.Add(actCommentBorderColor);

                Do(mi);
                IsModified = true;

                ppTitleBorderColor.IsOpen = false;
                return;
            }

            if (SelectedPictureBox != null)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "设置图片框边框色" };

                Action actPictureBoxBorderColor = new Action(SelectedPictureBox.ID, -1,
                    SelectedPictureBox.GetType().Name, "BorderColor",
                    ColorPicker.GetBrushName(SelectedPictureBox.BorderColor), e.EnName);

                SelectedPictureBox.BorderColor = e.PickedBrush;

                mi.Add(actPictureBoxBorderColor);

                Do(mi);
                IsModified = true;

                ppTitleBorderColor.IsOpen = false;
                return;
            }

            //设置标题边框色。
            if (Globals.SelectedTitlesList.Count > 0)
            {
                ModifingItem mi = new ModifingItem()
                {
                    IsNeedRefreshTitleAndLinesLocation = false,
                    ModifingName = "设置标题边框色"
                };

                foreach (Title t in Globals.SelectedTitlesList)
                {
                    Action actBorderColor = new Action(t.ID, -1, t.GetType().Name,
                        "BorderColor", ColorPicker.GetBrushName(t.BorderColor),
                        e.EnName);

                    t.BorderColor = e.PickedBrush;

                    mi.Add(actBorderColor);
                }

                Do(mi);
                IsModified = true;
            }

            ppTitleBorderColor.IsOpen = false;
        }

        private void bpBorderColor_CloseButtonClicked(object sender, MouseButtonEventArgs e)
        {
            //isPrepareInsertFixedTitle = false;//这两个不必要，因为需要按Ctrl或Shift键。
            //isPrepareInsertPictureBox = false;
            ppTitleBorderColor.IsOpen = false;//关闭边框色PopUp
        }

        private void ppTitleRelationLineColor_Opened(object sender, EventArgs e)
        {
            isPrepareInsertCommentOrLinkTitle = false;

            ppTitleBackground.IsOpen = false;
            ppTitleBorderColor.IsOpen = false;
            ppTitleForeground.IsOpen = false;
        }

        private void bpTitleRelationLineStroke_BrushPicked(object sender, LunarTools.BrushPickedEventArgs e)
        {
            if (SelectedRelation != null)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线色" };

                Action actLineColor = new Action(SelectedRelation.ID, -1, SelectedRelation.GetType().Name,
                    "LineColor", ColorPicker.GetBrushName(SelectedRelation.LineColor), e.EnName);

                SelectedRelation.LineColor = e.PickedBrush;

                mi.Add(actLineColor);

                Do(mi);
                IsModified = true;

                ppTitleRelationLineStroke.IsOpen = false;
                return;
            }

            if (Globals.SelectedTitlesList.Count > 0)
            {
                foreach (Title t in Globals.SelectedTitlesList)
                {
                    if (t.GetHigherLvelTitle() != Globals.MainWindow.RootTitle)
                    {
                        MessageBox.Show("只能对[一级标题]设置分支线色！", Globals.AppName,
                            MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                }

                ModifingItem mi = new ModifingItem()
                {
                    IsNeedRefreshTitleAndLinesLocation = false,
                    ModifingName = "设置分支线色"
                };

                foreach (Title t in Globals.SelectedTitlesList)
                {
                    Action actRelationLineStroke = new Action(t.ID, -1, t.GetType().Name,
                        "RelationLineStroke", ColorPicker.GetBrushName(t.RelationLineStroke),
                        e.EnName);

                    Action actBackground = new Action(t.ID, -1, t.GetType().Name,
                        "TitleBackground", ColorPicker.GetBrushName(t.TitleBackground),
                        e.EnName);

                    t.TitleBackground =
                        t.RelationLineStroke = e.PickedBrush;

                    mi.Add(actRelationLineStroke);
                    mi.Add(actBackground);  // 改变分支线时，默认同时更改一级标题的背景色。
                }

                Do(mi);
                IsModified = true;
            }

            ppTitleRelationLineStroke.IsOpen = false;
        }

        private void bpTitleRelationLineStroke_CloseButtonClicked(object sender, MouseButtonEventArgs e)
        {
            ppTitleRelationLineStroke.IsOpen = false;//关闭分支线色PopUp
        }

        private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            ppTitleForeground.IsOpen = false;
            ppTitleBackground.IsOpen = false;
            ppTitleBorderColor.IsOpen = false;

            if (e.MiddleButton == MouseButtonState.Pressed)
            {
                if (this.WindowState == WindowState.Maximized)
                {
                    this.WindowState = WindowState.Normal;
                }
                else if (this.WindowState == WindowState.Normal)
                {
                    this.WindowState = WindowState.Maximized;
                }
                e.Handled = true;
            }
        }

        private void mainScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            ppTitleForeground.IsOpen = false;
            ppTitleBackground.IsOpen = false;
            ppTitleBorderColor.IsOpen = false;


            if (mFile.IsSubmenuOpen || mView.IsSubmenuOpen || mEdit.IsSubmenuOpen ||
                mInsert.IsSubmenuOpen || mTools.IsSubmenuOpen || mHelp.IsSubmenuOpen)
            {
                return;//此时，快捷键应作用于菜单！！
            }

            //改变上、下、左、右键的行为。
            bool isShift = false;
            KeyStates lShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates rShift = Keyboard.GetKeyStates(Key.RightShift);
            if ((lShift & KeyStates.Down) > 0 || (rShift & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            bool isCtrl = false;
            KeyStates lCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates rCtrl = Keyboard.GetKeyStates(Key.RightCtrl);
            if ((lCtrl & KeyStates.Down) > 0 || (rCtrl & KeyStates.Down) > 0)
            {
                isCtrl = true;
            }

            bool isAlt = false;
            KeyStates lAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates rAlt = Keyboard.GetKeyStates(Key.RightAlt);
            if ((lAlt & KeyStates.Down) > 0 || (rAlt & KeyStates.Down) > 0)
            {
                isAlt = true;
            }

            switch (e.Key)
            {
                case Key.Up:
                    {
                        if (isShift == false)
                        {
                            if (isCtrl == false)
                            {
                                switch (this.View)
                                {
                                    case ViewMode.HorizontalList:
                                        {
                                            Commands.HorizontalListSelectUpCommand.Execute();
                                            break;
                                        }
                                    case ViewMode.VerticalList:
                                        {
                                            Commands.VerticalListSelectUpCommand.Execute();
                                            break;
                                        }
                                    default:
                                        {
                                            Commands.MindViewSelectUpCommand.Execute();
                                            if (this.IsPresentating == false)//分屏演示时计算较为复杂，没信心完成，还不如提供“全屏”功能。2013年9月6日
                                            {
                                                this.TryScrollTitle(this.MainSelectedTitle);
                                            }
                                            break;
                                        }
                                }
                            }
                            else
                            {
                                mainScrollViewer.ScrollToVerticalOffset(mainScrollViewer.VerticalOffset - 20);
                            }
                        }
                        else
                        {
                            if (isCtrl == false)
                            {
                                Commands.SetTextDockTopCommand.SetTextDockTop();
                            }
                        }

                        e.Handled = true;
                        break;
                    }
                case Key.Down:
                    {
                        if (isShift == false)
                        {
                            if (isCtrl == false)
                            {
                                switch (this.View)
                                {
                                    case ViewMode.HorizontalList:
                                        {
                                            Commands.HorizontalListSelectDownCommand.Execute();
                                            break;
                                        }
                                    case ViewMode.VerticalList:
                                        {
                                            Commands.VerticalListSelectDownCommand.Execute();
                                            break;
                                        }
                                    default:
                                        {
                                            Commands.MindViewSelectDownCommand.Execute();
                                            if (this.IsPresentating == false)
                                            {
                                                this.TryScrollTitle(this.MainSelectedTitle);
                                            }
                                            break;
                                        }
                                }
                            }
                            else
                            {
                                mainScrollViewer.ScrollToVerticalOffset(mainScrollViewer.VerticalOffset + 20);
                            }
                        }
                        else
                        {
                            if (isCtrl == false)
                            {
                                Commands.SetTextDockBottomCommand.SetTextDockBottom();
                            }
                        }

                        e.Handled = true;
                        break;
                    }
                case Key.Left:
                    {
                        if (isShift == false)
                        {
                            if (isCtrl == false)
                            {
                                switch (this.View)
                                {
                                    case ViewMode.HorizontalList:
                                        {
                                            Commands.HorizontalListSelectLeftCommand.Execute();
                                            break;
                                        }
                                    case ViewMode.VerticalList:
                                        {
                                            Commands.VerticalListSelectLeftCommand.Execute();
                                            break;
                                        }
                                    default:
                                        {
                                            Commands.MindViewSelectLeftCommand.Execute();
                                            if (this.IsPresentating == false)
                                            {
                                                this.TryScrollTitle(this.MainSelectedTitle);
                                            }
                                            break;
                                        }
                                }
                            }
                            else
                            {
                                mainScrollViewer.ScrollToHorizontalOffset(mainScrollViewer.HorizontalOffset - 20);
                            }
                        }
                        else
                        {
                            if (isCtrl == false)
                            {
                                Commands.SetTextDockLeftCommand.SetTextDockLeft();
                            }
                        }

                        e.Handled = true;
                        break;
                    }
                case Key.Right:
                    {
                        if (isShift == false)
                        {
                            if (isCtrl == false)
                            {
                                switch (this.View)
                                {
                                    case ViewMode.HorizontalList:
                                        {
                                            Commands.HorizontalListSelectRightCommand.Execute();
                                            break;
                                        }
                                    case ViewMode.VerticalList:
                                        {
                                            Commands.VerticalListSelectRightCommand.Execute();
                                            break;
                                        }
                                    default:
                                        {
                                            Commands.MindViewSelectRightCommand.Execute();
                                            if (this.IsPresentating == false)
                                            {
                                                this.TryScrollTitle(this.MainSelectedTitle);
                                            }
                                            break;
                                        }
                                }
                            }
                            else
                            {
                                mainScrollViewer.ScrollToHorizontalOffset(mainScrollViewer.HorizontalOffset + 20);
                            }
                        }
                        else
                        {
                            if (isCtrl == false)
                            {
                                Commands.SetTextDockRightCommand.SetTextDockRight();
                            }
                        }

                        e.Handled = true;
                        break;
                    }
                case Key.OemPeriod://句点键，即>键
                    {
                        if (tbtnFullScreen.IsChecked == true)
                        {
                            //全屏状态下，Tab/Enter/PageUp/PageDown键的行为不同。
                            //句点键，演示试题集或跳过提问（演示笔上长按PageDown会输出句点键）。
                            if (this.MainSelectedTitle != null)
                            {
                                if (this.MainSelectedTitle.HasQuestion)
                                {
                                    this.MainSelectedTitle.PresentateQuestions();
                                }
                                else
                                {
                                    SelectNextOrShowFillBlank(true);
                                }
                            }

                            e.Handled = true;
                        }
                        else
                        {
                            if (isShift == false)
                            {
                                if (isCtrl)
                                {
                                    if (isAlt)
                                    {
                                        Commands.MoveDownBetweenSameLevelCommand.MoveDown();
                                        e.Handled = true;
                                    }
                                }
                            }
                        }
                        break;
                    }
                case Key.OemComma://逗号键，即<键
                    {
                        if (isShift == false)
                        {
                            if (isCtrl)
                            {
                                if (isAlt)
                                {
                                    Commands.MoveUpBetweenSameLevelCommand.MoveUp();
                                    e.Handled = true;
                                }
                            }
                        }
                        break;
                    }
                case Key.Enter:
                    {
                        if (ckxAutoPresentation.IsChecked == true)
                        {
                            //Enter键用于编辑。
                            //只要按下Enter或鼠标上的某个键，都会退出“计时滚动”状态。
                            ckxAutoPresentation.IsChecked = false;
                            menuLine.Height = new GridLength(20);
                            toolBarLine.Height = new GridLength(0, GridUnitType.Auto);
                            statusLine.Height = new GridLength(32);
                            bottomGridSplitter.Height = 6;

                            ckxAutoPresentation_Click(null, null);
                        }
                        else
                        {
                            if (tbtnFullScreen.IsChecked == true)
                            {
                                if (isCtrl)
                                {
                                    if (isShift)
                                    {
                                        //Ctrl+Shift+Enter编辑后台试题文本

                                        btnEditorQuestionsOfTitle_Click(sender, e);
                                    }
                                    else
                                    {
                                        //Ctrl+Enter
                                        Commands.EditCommentTextCommand.EditCommentTextOfTitle();
                                    }
                                }
                                else
                                {
                                    //非Ctrl
                                    if (isShift)
                                    {
                                        //Shift+Enter
                                        Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();
                                    }
                                    else
                                    {
                                        //Enter，为什么在全屏时Enter显示|隐藏标题的填空项文本而不是编辑？
                                        //这是因为PPT演示笔实在功能太少了，只支持几个极其有限的快捷键啊！！！
                                        Commands.EditTitleTextCommand.EditTitleTextOfSwitchCommentVisibleWhenFullScreen();
                                    }
                                }
                                e.Handled = true;
                            }
                            else
                            {
                                if (!isCtrl)
                                {
                                    if (isShift)
                                    {
                                        if (View == ViewMode.HorizontalList && MainSelectedTitle != null && RootTitle != null && MainSelectedTitle.HigherLevelTitleID == RootTitle.ID)
                                        {
                                            Commands.InsertLowerLevelTitleCommand.InsertLowerLevelTitle();//横向列表、SubRoot标题，按Shift+Enter改为添加下级标题（这样视觉效果向下，更好理解）。
                                        }
                                        else
                                        {
                                            Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();//正常情况下，Shift+Enter添加的是下级标题。（视觉方向向下。）
                                        }

                                        e.Handled = true;//否则会引发Window_TextInput事件，因为回车键会输出\r字符。
                                    }
                                }
                                else
                                {
                                    //Ctrl+Shift+Enter编辑后台试题文本
                                    if (isShift)
                                    {
                                        btnEditorQuestionsOfTitle_Click(sender, e);
                                    }
                                    else
                                    {
                                        Commands.EditCommentTextCommand.EditCommentTextOfTitle();
                                    }
                                    e.Handled = true;
                                }
                                //else//Enter用于编辑标题文本。
                            }
                        }
                        break;
                    }
                case Key.Tab:
                    {
                        if (tbtnFullScreen.IsChecked == true)
                        {
                            //全屏状态下Tab/Enter/PageDown/PageUp这四个键的行为有所不同。
                            //查看标题中的图标
                            //要考虑一个标题中存在多个图标的情况。

                            //Tab用于演示标题中的图像数据。
                            if (this.MainSelectedTitle != null)
                            {
                                if (this.MainSelectedTitle.TitleImagesCount <= 0)
                                {
                                    //提问某学生
                                    this.AskSomeoneAQuestion();
                                    TryScrollTitle(this.MainSelectedTitle, false);
                                }
                                else
                                {
                                    this.MainSelectedTitle.PresentateImages();
                                }
                            }
                        }
                        else
                        {
                            if (isShift)
                            {
                                Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();
                            }
                            else
                            {
                                //非全屏状态下，Tab与Insert功能相同。Tab的视觉效果是向右的。一般用于添加下级标题（在右下方）。

                                if (View == ViewMode.HorizontalList && MainSelectedTitle != null && RootTitle != null && MainSelectedTitle.HigherLevelTitleID == RootTitle.ID)
                                {
                                    Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();//横向列表、SubRoot标题，按Tab改为添加同级标题（这样视觉效果是向右，更好理解）。
                                }
                                else
                                {
                                    Commands.InsertLowerLevelTitleCommand.InsertLowerLevelTitle();
                                }
                            }
                        }

                        e.Handled = true;//否则会导致焦点转移，无法直接向标题输入文本。

                        break;
                    }
                case Key.B:
                    {
                        if (isCtrl && !isAlt && !isShift)
                        {
                            miInsertLines_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.Insert:
                    {
                        if (isShift)
                        {
                            Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();
                        }
                        else
                        {
                            //Tab的视觉效果是向右的。一般用于添加下级标题（在右下方）。

                            if (View == ViewMode.HorizontalList && MainSelectedTitle != null && RootTitle != null && MainSelectedTitle.HigherLevelTitleID == RootTitle.ID)
                            {
                                Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();//横向列表、SubRoot标题，按Tab改为添加同级标题（这样视觉效果是向右，更好理解）。
                            }
                            else
                            {
                                Commands.InsertLowerLevelTitleCommand.InsertLowerLevelTitle();
                            }
                        }

                        break;
                    }
            }
        }

        private void SwitchMenuBarAndStatusBar()
        {
            if (menuLine.Height.Value > 0)
            {
                statusLine.Height =
                toolBarLine.Height =
                menuLine.Height = new GridLength(0);
                rightToolGridColumn.Width = new GridLength(0);
                bottomGridSplitter.Height = 0;
                this.WindowStyle = System.Windows.WindowStyle.None;
                this.mainScrollViewer.VerticalScrollBarVisibility =
                this.mainScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                this.baseBorder.Margin = new Thickness(0);
                this.WindowState = System.Windows.WindowState.Maximized;
            }
            else
            {
                menuLine.Height = new GridLength(20);
                toolBarLine.Height = new GridLength(0, GridUnitType.Auto);
                statusLine.Height = new GridLength(32);
                //toolGridColumn.Width = new GridLength(330);

                bottomGridSplitter.Height = 6;
                this.WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
                this.mainScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
                this.mainScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
                this.baseBorder.Margin = new Thickness(10);
                this.WindowState = System.Windows.WindowState.Normal;
            }
        }

        /// <summary>
        /// 全屏状态下Tab/Enter/PageDown/PageUp这四个键的行为有所不同。
        /// 
        /// 如果标题中有填空项（带下划线的文本），且当前未显示填空项的答案，则显示其答案。
        /// 如果标题中没有填空项，或已显示填空项答案，则选定下一个标题。
        /// </summary>
        public void SelectNextOrShowFillBlank(bool ignoreAutoAskAction = false)
        {
            if (this.MainSelectedTitle == null)
            {
                if (this.SelectedComment != null)
                {
                    if (this.SelectedComment.MasterTitle != null)
                    {
                        this.SelectedComment.MasterTitle.SelectOnlySelf();
                        return;
                    }
                }

                if (this.SelectedPictureBox != null)
                {
                    if (this.SelectedPictureBox.MasterTitle != null)
                    {
                        this.SelectedPictureBox.MasterTitle.SelectOnlySelf();
                        return;
                    }
                }

                if (this.RootTitle != null)
                {
                    this.RootTitle.SelectOnlySelf();
                    TryScrollTitle(this.RootTitle);
                }
                return;
            }

            if (this.MainSelectedTitle.TextDock == Dock.Right)//图在左边，文本在右，就先演示图片。
            {
                if (this.MainSelectedTitle.ImagesPresentated == false && this.MainSelectedTitle.HasTitleImage)
                {
                    this.MainSelectedTitle.PresentateImages();
                    this.MainSelectedTitle.ImagesPresentated = true;
                    return;
                }
            }

            if (this.MainSelectedTitle.IsFixed)
            {
                var master = this.MainSelectedTitle.GetHigherLvelTitle();
                if (master != null)
                {
                    master.SelectOnlySelf();
                    TryScrollTitle(master);
                }
                return;
            }

            //全屏演示模式下,如果是填空,根据填空状态来决定：①显示填空答案，②如果已经显示出来，选取下一个标题。
            if (tbtnFullScreen.IsChecked == true)
            {
                if (ignoreAutoAskAction == false)
                {
                    if (miAutoAskForFillBlanks.IsChecked && askPanel.Visibility != System.Windows.Visibility.Visible)
                    {
                        if (this.MainSelectedTitle.HasFillBlank && this.MainSelectedTitle.IsNoneFillBlankVisible)
                        {
                            this.AskSomeoneAQuestion();
                            TryScrollTitle(this.MainSelectedTitle, false);
                            return;
                        }
                    }
                }

                if (miShowWholeTitleFillBlanks.IsChecked)
                {
                    if (this.MainSelectedTitle.HasFillBlank && this.MainSelectedTitle.IsFillBlankVisible == false)
                    {
                        this.MainSelectedTitle.IsFillBlankVisible = true;
                        return;
                    }
                }
                else
                {
                    if (this.MainSelectedTitle.HasFillBlank && this.MainSelectedTitle.IsFillBlankVisible == false)
                    {
                        if (this.MainSelectedTitle.ShowNextFillBlankTextOrPrentatateImages()) return;//如果啥都没做，继续。
                    }
                    else
                    {
                        if (this.MainSelectedTitle.TextDock != Dock.Right)
                        {
                            if (this.MainSelectedTitle.HasTitleImage &&
                                this.MainSelectedTitle.ImagesPresentated == false)
                            {
                                this.MainSelectedTitle.PresentateImages();
                                this.MainSelectedTitle.ImagesPresentated = true;
                            }
                        }
                    }
                }
            }

            if (this.MainSelectedTitle.CollapsedLowers == true)
            {
                SwitchTitleCollapsedCommand.SwitchTitleCollapsed();
            }

            //如果有概要的话，先显示概要，这串代码不能加在此处，而只能针对具体快捷键（左、右），
            //这是因为这样会造成按PageDown时在标题与标题概要之间不断循环跳转，无法进入到下一个标题。
            //var summaryComment = this.MainSelectedTitle.SummaryComment;
            //if (summaryComment != null)
            //{
            //    summaryComment.SelectOnlySelf();
            //    return;
            //}

            var next = GetTitleByGlobalIndex(this.MainSelectedTitle.GlobalIndex + 1);
            if (next != null)
            {
                next.SelectOnlySelf();
                TryScrollTitle(next);
            }
            else
            {
                //if (this.RootTitle != null)
                //{
                //    this.RootTitle.SelectOnlySelf();
                //    TryScrollTitle(this.RootTitle);
                //}
                //不应该这样子做。这会自动跳回原先的位置，不方便。

                //停用定时器
                if (ckxAutoPresentation.IsChecked == true)
                {
                    ckxAutoPresentation.IsChecked = false;
                    ckxAutoPresentation_Click(null, null);

                    //MessageBox.Show("计时滚动结束！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                    //不利于学生抄写最后一部分内容，弹出的消息框会挡住一些字。

                    if (menuLine.Height.Value <= 0)
                    {
                        SwitchMenuBarAndStatusBar();
                    }
                }
            }

            if (askPanel.Visibility != System.Windows.Visibility.Collapsed)
                askPanel.Visibility = System.Windows.Visibility.Collapsed;
        }

        /// <summary>
        /// 给每个Title指定一个全局索引，以便于PageDown键在标题间跳跃。
        /// </summary>
        /// <param name="title"></param>
        /// <param name="newStartIndex"></param>
        /// <returns></returns>
        private int RefreshGlobleIndexes(Title title, int newStartIndex)
        {
            if (title == null) return -1;

            title.GlobalIndex = newStartIndex;
            var children = title.GetLowerLevelTitles();
            foreach (var child in children)
            {
                child.GlobalIndex = newStartIndex + 1;
                newStartIndex = RefreshGlobleIndexes(child, newStartIndex + 1);
            }

            return newStartIndex;
        }

        private Title GetNextBrother(Title title)
        {
            if (title == null) return null;

            var higherTitle = title.GetHigherLvelTitle();
            if (higherTitle == null) return null;

            var brothers = higherTitle.GetLowerLevelTitles();
            if (brothers.Count <= 0 || brothers.Contains(title) == false) return null;

            int index = brothers.IndexOf(title);
            if (index < brothers.Count - 1)
            {
                return brothers[index + 1];
            }
            else return null;
        }

        private Title GetFirstChild(Title title)
        {
            if (title == null) return null;

            var children = title.GetLowerLevelTitles();
            if (children.Count <= 0) return null;

            return children[0];
        }

        /// <summary>
        /// 全屏状态下，Tab/Enter/PageUp/PageDown这四个键的行为有所不同。
        /// 如果有同级上一个，取之，如果没有同级上一个，取上级
        /// </summary>
        public void SelectPreview()
        {
            if (this.MainSelectedTitle == null)
            {
                if (this.SelectedComment != null)
                {
                    if (this.SelectedComment.MasterTitle != null)
                    {
                        this.SelectedComment.MasterTitle.SelectOnlySelf();
                        return;
                    }
                }

                if (this.SelectedPictureBox != null)
                {
                    if (this.SelectedPictureBox.MasterTitle != null)
                    {
                        this.SelectedPictureBox.MasterTitle.SelectOnlySelf();
                        return;
                    }
                }

                if (this.RootTitle != null)
                {
                    this.RootTitle.SelectOnlySelf();
                    TryScrollTitle(this.RootTitle);
                }
                return;
            }

            if (this.MainSelectedTitle.IsFixed)
            {
                var master = this.MainSelectedTitle.GetHigherLvelTitle();
                if (master != null)
                {
                    master.SelectOnlySelf();
                    TryScrollTitle(master);
                }
                return;
            }

            //向后选取才需要
            //if (this.MainSelectedTitle.CollapsedLowers == true)
            //{
            //    SwitchTitleCollapsedCommand.SwitchTitleCollapsed();
            //}

            var previewTitle = GetTitleByGlobalIndex(this.MainSelectedTitle.GlobalIndex - 1);
            if (previewTitle != null)
            {
                if (previewTitle.BeCollapsed == false)
                {
                    previewTitle.SelectOnlySelf();
                    TryScrollTitle(previewTitle);
                }
                else
                {
                    while (previewTitle.BeCollapsed)
                    {
                        previewTitle = previewTitle.GetHigherLvelTitle();
                        if (previewTitle == null) break;
                    }

                    if (previewTitle != null)
                    {
                        previewTitle.SelectOnlySelf();
                        TryScrollTitle(previewTitle);
                    }
                }
            }
            else
            {
                if (this.RootTitle != null)
                {
                    this.RootTitle.SelectOnlySelf();
                    TryScrollTitle(this.RootTitle);
                }
            }

            #region 原先是选取“上一级”而不是“上一个”。还是“上一个”更容易理解。
            //if (this.MainSelectedTitle == null)
            //{
            //    if (this.SelectedComment != null)
            //    {
            //        if (this.SelectedComment.MasterTitle != null)
            //        {
            //            this.SelectedComment.MasterTitle.SelectOnlySelf();
            //            return;
            //        }
            //    }

            //    if (this.SelectedPictureBox != null)
            //    {
            //        if (this.SelectedPictureBox.MasterTitle != null)
            //        {
            //            this.SelectedPictureBox.MasterTitle.SelectOnlySelf();
            //            return;
            //        }
            //    }

            //    if (this.RootTitle != null)
            //    {
            //        this.RootTitle.SelectOnlySelf();
            //        TryToScrollRootTitleAtCenter();
            //    }
            //    return;
            //}

            //if (this.MainSelectedTitle.IsFixed)
            //{
            //    var master = this.MainSelectedTitle.GetHigherLvelTitle();
            //    if (master != null)
            //    {
            //        master.SelectOnlySelf();
            //    }
            //    return;
            //}

            //var fatherTitle = this.MainSelectedTitle.GetHigherLvelTitle();
            //if (fatherTitle == null) return;

            ////取兄弟，若无位置靠前的兄弟，则取父标题
            //var brothers = fatherTitle.GetLowerLevelTitles();
            //if (brothers == null || brothers.Count <= 0)
            //{
            //    fatherTitle.SelectOnlySelf();
            //    TryScrollTitle(fatherTitle);
            //    return;
            //}

            //int index = brothers.IndexOf(this.MainSelectedTitle);
            //if (index <= 0)
            //{
            //    fatherTitle.SelectOnlySelf();
            //    TryScrollTitle(fatherTitle);
            //    return;
            //}

            //brothers[index - 1].SelectOnlySelf();
            //TryScrollTitle(brothers[index - 1]); 
            #endregion

            if (askPanel.Visibility != System.Windows.Visibility.Collapsed) askPanel.Visibility = System.Windows.Visibility.Collapsed;
        }

        private void Window_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            bool isShift = false;

            KeyStates lShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates rShift = Keyboard.GetKeyStates(Key.RightShift);

            if ((lShift & KeyStates.Down) > 0 || (rShift & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            if (isShift || e.RightButton == MouseButtonState.Pressed)
            {
                //按住右键滚动可以左右卷动。

                double offset = mainScrollViewer.HorizontalOffset;

                mainScrollViewer.ScrollToHorizontalOffset(offset -= e.Delta);

                e.Handled = true;
                return;
            }

            bool isCtrl = false;

            KeyStates lCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates rCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            if ((lCtrl & KeyStates.Down) > 0 || (rCtrl & KeyStates.Down) > 0)
            {
                isCtrl = true;
            }

            if (isCtrl)
            {
                sliderOfCanvasScale.Value += (e.Delta > 0) ? 0.2 : -0.2;
                e.Handled = true;
            }
        }

        private Relation selectedRelation = null;

        public Relation SelectedRelation
        {
            get { return selectedRelation; }
            set
            {
                selectedRelation = value;

                if (selectedRelation == null)
                {
                    start.Visibility = Visibility.Hidden;
                    end.Visibility = Visibility.Hidden;
                    startCtrl.Visibility = Visibility.Hidden;
                    endCtrl.Visibility = Visibility.Hidden;

                    startCtrlLine.Visibility = Visibility.Hidden;
                    endCtrlLine.Visibility = Visibility.Hidden;
                }
                else
                {
                    if (SelectedTitleImage != null) SelectedTitleImage = null;
                    if (selectedComment != null) SelectedComment = null;
                    if (selectedPictureBox != null) SelectedPictureBox = null;

                    if (SelectedTitlesList.Count > 0) ClearAllFromSelectedTitleList();

                    Title startTitle = selectedRelation.StartTitle;
                    Title endTitle = selectedRelation.EndTitle;

                    double startIndentWidth = startTitle.IndentWidth;
                    double endIndentWidth = endTitle.IndentWidth;

                    Rect startRect = new Rect(startTitle.LeftTop.X + startTitle.MainBorder.Margin.Left +
                        ((startTitle.Direction == TitleDirection.Right) ? startIndentWidth : -startIndentWidth),
                        startTitle.LeftTop.Y + startTitle.MainBorder.Margin.Top,
                        startTitle.ActualWidth + startTitle.MainBorder.Margin.Left + startTitle.MainBorder.Margin.Right,
                        startTitle.ActualHeight + startTitle.MainBorder.Margin.Top + startTitle.MainBorder.Margin.Bottom);


                    double w, h;
                    w = endTitle.ActualWidth + endTitle.MainBorder.Margin.Left + endTitle.MainBorder.Margin.Right;
                    h = endTitle.ActualHeight + endTitle.MainBorder.Margin.Top + endTitle.MainBorder.Margin.Bottom;

                    Rect endRect = new Rect(endTitle.LeftTop.X + endTitle.MainBorder.Margin.Left +
                        ((endTitle.Direction == TitleDirection.Right) ? endIndentWidth : -endIndentWidth),
                        endTitle.LeftTop.Y + endTitle.MainBorder.Margin.Top,
                        w,
                        h);

                    Point startCenter = new Point(startRect.Left + (startRect.Right - startRect.Left) / 2,
                        startRect.Top + (startRect.Bottom - startRect.Top) / 2);

                    Point endCenter = new Point(endRect.Left + (endRect.Right - endRect.Left) / 2,
                        endRect.Top + (endRect.Bottom - endRect.Top) / 2);

                    Point startCtrlPt = new Point(selectedRelation.StartCenter.X + selectedRelation.StartCtrlPoint.X,
                        selectedRelation.StartCenter.Y + selectedRelation.StartCtrlPoint.Y);
                    Point endCtrlPt = new Point(selectedRelation.EndCenter.X + selectedRelation.EndCtrlPoint.X,
                        selectedRelation.EndCenter.Y + selectedRelation.EndCtrlPoint.Y);

                    //防止被意外删除。
                    if (mainCanvas.Children.Contains(start) == false)
                    {
                        mainCanvas.Children.Add(start);
                    }

                    if (mainCanvas.Children.Contains(startCtrl) == false)
                    {
                        mainCanvas.Children.Add(startCtrl);
                    }

                    if (mainCanvas.Children.Contains(end) == false)
                    {
                        mainCanvas.Children.Add(end);
                    }

                    if (mainCanvas.Children.Contains(endCtrl) == false)
                    {
                        mainCanvas.Children.Add(endCtrl);
                    }

                    if (mainCanvas.Children.Contains(startCtrlLine) == false)
                    {
                        mainCanvas.Children.Add(startCtrlLine);
                    }

                    if (mainCanvas.Children.Contains(endCtrlLine) == false)
                    {
                        mainCanvas.Children.Add(endCtrlLine);
                    }

                    Canvas.SetLeft(start, selectedRelation.OuterStartPoint.X - 5);
                    Canvas.SetTop(start, selectedRelation.OuterStartPoint.Y - 5);

                    Canvas.SetLeft(end, selectedRelation.OuterEndPoint.X - 5);
                    Canvas.SetTop(end, selectedRelation.OuterEndPoint.Y - 5);

                    Canvas.SetLeft(startCtrl, startCtrlPt.X - 5);
                    Canvas.SetTop(startCtrl, startCtrlPt.Y - 5);

                    Canvas.SetLeft(endCtrl, endCtrlPt.X - 5);
                    Canvas.SetTop(endCtrl, endCtrlPt.Y - 5);

                    startCtrlLine.X1 = selectedRelation.OuterStartPoint.X; startCtrlLine.Y1 = selectedRelation.OuterStartPoint.Y;
                    startCtrlLine.X2 = startCtrlPt.X; startCtrlLine.Y2 = startCtrlPt.Y;

                    endCtrlLine.X1 = selectedRelation.OuterEndPoint.X; endCtrlLine.Y1 = selectedRelation.OuterEndPoint.Y;
                    endCtrlLine.X2 = endCtrlPt.X; endCtrlLine.Y2 = endCtrlPt.Y;

                    start.Visibility = Visibility.Visible;
                    end.Visibility = Visibility.Visible;
                    startCtrl.Visibility = Visibility.Visible;
                    endCtrl.Visibility = Visibility.Visible;

                    startCtrlLine.Visibility = Visibility.Visible;
                    endCtrlLine.Visibility = Visibility.Visible;

                    rotateSlider.Value = selectedRelation.RotateAngle;

                    selectedRelation.RefreshTbxCaret();
                }
            }
        }

        private int defaultFontSize = 15;
        //一级标题默认字号为全局默认字号的6/5。
        public int DefaultFontSize
        {
            get
            {
                return defaultFontSize;
            }

            set
            {
                defaultFontSize = value;
                if (this.mainXmlDocument != null &&
                    this.mainXmlDocument.DocumentElement != null)
                {
                    this.mainXmlDocument.DocumentElement.SetAttribute("DefaultFontSize", value.ToString());
                }

                RefreshDefaultFontSize();
                RefreshTitleAndLinesLocation();
            }
        }

        private void RefreshDefaultFontSize()
        {
            foreach (UIElement ue in this.mainCanvas.Children)
            {
                Title title = ue as Title;
                if (title != null)
                {
                    title.BuildTitle();
                    continue;
                }

                Comment comment = ue as Comment;
                if (comment != null)
                {
                    comment.BuildComment();
                    continue;
                }
            }
            RefreshDefaultFontSizeMenuItemsState();
        }

        private void RefreshDefaultFontSizeMenuItemsState()
        {
            switch (this.defaultFontSize)
            {
                case 20:
                    {
                        miDefaultFontSizeMiddle.IsChecked = true;
                        miDefaultFontSizeLarge.IsChecked = miDefaultFontSizeSmall.IsChecked = false;
                        break;
                    }
                case 25:
                    {
                        miDefaultFontSizeLarge.IsChecked = true;
                        miDefaultFontSizeSmall.IsChecked = miDefaultFontSizeMiddle.IsChecked = false;
                        break;
                    }
                default:
                    {
                        miDefaultFontSizeSmall.IsChecked = true;
                        miDefaultFontSizeLarge.IsChecked = miDefaultFontSizeMiddle.IsChecked = false;
                        break;
                    }
            }
        }

        public bool isDraggingPictureBox = false;

        private Point pictureBoxDraggingSize = new Point(0, 0);

        public void SetPictureBoxDraggingSize(Point pt)
        {
            pictureBoxDraggingSize = pt;
        }

        public bool isDraggingComment = false;

        private Point commentDraggingSize = new Point(0, 0);

        public void SetCommentDraggingSize(Point pt)
        {
            commentDraggingSize = pt;
        }

        public bool isDraggingFixedTitle = false;

        private Point fixedTitleDraggingSize = new Point(0, 0);

        public void SetFixedTitleDraggingSize(Point pt)
        {
            fixedTitleDraggingSize = pt;
        }

        private bool isDraggingStartCtrl = false;

        private bool isDraggingEndCtrl = false;

        private void startCtrl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isDraggingStartCtrl = true;
            isDraggingEndCtrl = false;
            e.Handled = true;
        }

        private void endCtrl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isDraggingStartCtrl = false;
            isDraggingEndCtrl = true;
            e.Handled = true;
        }

        private void startCtrl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (selectedRelation == null) return;

            Title startTitle = selectedRelation.StartTitle;
            if (startTitle == null) return;

            double startIndentWidth =
                (startTitle.Direction == TitleDirection.Right) ?
                startTitle.IndentWidth :
                -startTitle.IndentWidth;

            Rect startRect = new Rect(startTitle.LeftTop.X + startTitle.MainBorder.Margin.Left + startIndentWidth,
                startTitle.LeftTop.Y + startTitle.MainBorder.Margin.Top,
                startTitle.ActualWidth - startTitle.MainBorder.Margin.Left - startTitle.MainBorder.Margin.Right,
                startTitle.ActualHeight - startTitle.MainBorder.Margin.Top - startTitle.MainBorder.Margin.Bottom);

            Point startCenter = new Point(startRect.Left + (startRect.Right - startRect.Left) / 2,
                startRect.Top + (startRect.Bottom - startRect.Top) / 2);

            Point mousePoint = e.GetPosition(mainCanvas);
            PointToRect.ArrowPoints aptStart = PointToRect.GetCrossPointToRect(startRect, mousePoint);

            Point newCtrlPoint = new Point(mousePoint.X - startCenter.X, mousePoint.Y - startCenter.Y);

            XmlAttribute attrStartCtrlPoint = selectedRelation.XmlData.GetAttribute("StartCtrlPoint");
            if (attrStartCtrlPoint != null)
            {
                Point oldBackXmlPoint = Point.Parse(attrStartCtrlPoint.Value);

                if (newCtrlPoint.X >= oldBackXmlPoint.X - 5 && newCtrlPoint.X <= oldBackXmlPoint.X + 5 &&
                newCtrlPoint.Y >= oldBackXmlPoint.Y - 5 && newCtrlPoint.Y <= oldBackXmlPoint.Y + 5)
                {
                    isDraggingStartCtrl = false;
                    return;
                }
            }

            ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线首控制点坐标" };

            Action actStartCtrl;

            XmlAttribute attrStartCtrl = selectedRelation.XmlData.GetAttribute("StartCtrlPoint");
            if (attrStartCtrl != null)
            {
                actStartCtrl = new Action(selectedRelation.ID, -1, selectedRelation.GetType().Name,
                   "StartCtrlPoint", attrStartCtrl.Value, newCtrlPoint.ToString());
            }
            else
            {
                actStartCtrl = new Action(selectedRelation.ID, -1, selectedRelation.GetType().Name,
                   "StartCtrlPoint", selectedRelation.StartCtrlPoint.ToString(), newCtrlPoint.ToString());
            }

            selectedRelation.StartCtrlPoint = newCtrlPoint;

            mi.Add(actStartCtrl);

            Canvas.SetLeft(startCtrl, mousePoint.X - 5);
            Canvas.SetTop(startCtrl, mousePoint.Y - 5);

            Canvas.SetLeft(start, aptStart.Top.X - 5);
            Canvas.SetTop(start, aptStart.Top.Y - 5);

            startCtrlLine.X1 = mousePoint.X; startCtrlLine.Y1 = mousePoint.Y;
            startCtrlLine.X2 = aptStart.Top.X; startCtrlLine.Y2 = aptStart.Top.Y;

            Do(mi);
            IsModified = true;

            e.Handled = true;
            isDraggingStartCtrl = false;
        }

        private void endCtrl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (selectedRelation == null) return;

            Title endTitle = selectedRelation.EndTitle;
            if (endTitle == null) return;

            double endIndentWidth =
                (endTitle.Direction == TitleDirection.Right) ?
                endTitle.IndentWidth :
                -endTitle.IndentWidth;

            Rect endRect = new Rect(endTitle.LeftTop.X + endTitle.MainBorder.Margin.Left + endIndentWidth,
                endTitle.LeftTop.Y + endTitle.MainBorder.Margin.Top,
                endTitle.ActualWidth - endTitle.MainBorder.Margin.Left - endTitle.MainBorder.Margin.Right,
                endTitle.ActualHeight - endTitle.MainBorder.Margin.Top - endTitle.MainBorder.Margin.Bottom);

            Point endCenter = new Point(endRect.Left + (endRect.Right - endRect.Left) / 2,
                endRect.Top + (endRect.Bottom - endRect.Top) / 2);

            Point mousePoint = e.GetPosition(mainCanvas);
            PointToRect.ArrowPoints aptEnd = PointToRect.GetCrossPointToRect(endRect, mousePoint);

            Point newCtrlPoint = new Point(mousePoint.X - endCenter.X, mousePoint.Y - endCenter.Y);

            XmlAttribute attrStartCtrlPoint = selectedRelation.XmlData.GetAttribute("EndCtrlPoint");
            if (attrStartCtrlPoint != null)
            {
                Point oldBackXmlPoint = Point.Parse(attrStartCtrlPoint.Value);

                if (newCtrlPoint.X >= oldBackXmlPoint.X - 5 && newCtrlPoint.X <= oldBackXmlPoint.X + 5 &&
                    newCtrlPoint.Y >= oldBackXmlPoint.Y - 5 && newCtrlPoint.Y <= oldBackXmlPoint.Y + 5)
                {
                    isDraggingEndCtrl = false;
                    return;
                }
            }

            ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线尾控制点坐标" };

            Action actEndCtrl;
            XmlAttribute attrEndCtrl = selectedRelation.XmlData.GetAttribute("EndCtrlPoint");
            if (attrEndCtrl != null)
            {
                actEndCtrl = new Action(selectedRelation.ID, -1, selectedRelation.GetType().Name,
                   "EndCtrlPoint", attrEndCtrl.Value, newCtrlPoint.ToString());
            }
            else
            {
                actEndCtrl = new Action(selectedRelation.ID, -1, selectedRelation.GetType().Name,
                   "EndCtrlPoint", selectedRelation.EndCtrlPoint.ToString(), newCtrlPoint.ToString());
            }

            selectedRelation.EndCtrlPoint = newCtrlPoint;

            mi.Add(actEndCtrl);

            Canvas.SetLeft(endCtrl, mousePoint.X - 5);
            Canvas.SetTop(endCtrl, mousePoint.Y - 5);

            Canvas.SetLeft(end, aptEnd.Top.X - 5);
            Canvas.SetTop(end, aptEnd.Top.Y - 5);

            endCtrlLine.X1 = mousePoint.X; endCtrlLine.Y1 = mousePoint.Y;
            endCtrlLine.X2 = aptEnd.Top.X; endCtrlLine.Y2 = aptEnd.Top.Y;

            Do(mi);
            IsModified = true;

            e.Handled = true;
            isDraggingEndCtrl = false;
        }

        internal bool isResizeCommentWidth = false;

        private void mainScrollViewer_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (selectedRelation == null && SelectedComment == null &&
                SelectedPictureBox == null && SelectedTitlesList.Count <= 0 &&
                selectedTitleImage == null)
                return;

            if (isDraggingStartCtrl)
            {
                Title startTitle = selectedRelation.StartTitle;

                double startIndentWidth =
                    (startTitle.Direction == TitleDirection.Right) ?
                    startTitle.IndentWidth :
                    -startTitle.IndentWidth;

                Rect startRect = new Rect(startTitle.LeftTop.X + startTitle.MainBorder.Margin.Left + startIndentWidth,
                    startTitle.LeftTop.Y + startTitle.MainBorder.Margin.Top,
                    startTitle.ActualWidth - startTitle.MainBorder.Margin.Left - startTitle.MainBorder.Margin.Right,
                    startTitle.ActualHeight - startTitle.MainBorder.Margin.Top - startTitle.MainBorder.Margin.Bottom);

                Point startCenter = new Point(startRect.Left + (startRect.Right - startRect.Left) / 2,
                    startRect.Top + (startRect.Bottom - startRect.Top) / 2);

                Point mousePoint = e.GetPosition(mainCanvas);
                PointToRect.ArrowPoints aptStart = PointToRect.GetCrossPointToRect(startRect, mousePoint);

                selectedRelation.SetStartCtrlPointWhenMouseMove(new Point(mousePoint.X - startCenter.X, mousePoint.Y - startCenter.Y));

                Canvas.SetLeft(startCtrl, mousePoint.X - 5);
                Canvas.SetTop(startCtrl, mousePoint.Y - 5);

                Canvas.SetLeft(start, aptStart.Top.X - 5);
                Canvas.SetTop(start, aptStart.Top.Y - 5);

                startCtrlLine.X1 = mousePoint.X; startCtrlLine.Y1 = mousePoint.Y;
                startCtrlLine.X2 = aptStart.Top.X; startCtrlLine.Y2 = aptStart.Top.Y;

                //e.Handled = true;
            }
            else if (isDraggingEndCtrl)
            {
                Title endTitle = selectedRelation.EndTitle;
                double endIndentWidth =
                    (endTitle.Direction == TitleDirection.Right) ?
                    endTitle.IndentWidth :
                    -endTitle.IndentWidth;

                Rect endRect = new Rect(endTitle.LeftTop.X + endTitle.MainBorder.Margin.Left + endIndentWidth,
                    endTitle.LeftTop.Y + endTitle.MainBorder.Margin.Top,
                    endTitle.ActualWidth - endTitle.MainBorder.Margin.Left - endTitle.MainBorder.Margin.Right,
                    endTitle.ActualHeight - endTitle.MainBorder.Margin.Top - endTitle.MainBorder.Margin.Bottom);

                Point endCenter = new Point(endRect.Left + (endRect.Right - endRect.Left) / 2,
                    endRect.Top + (endRect.Bottom - endRect.Top) / 2);

                Point mousePoint = e.GetPosition(mainCanvas);
                PointToRect.ArrowPoints aptEnd = PointToRect.GetCrossPointToRect(endRect, mousePoint);

                selectedRelation.SetEndCtrlPointWhenMouseMove(new Point(mousePoint.X - endCenter.X, mousePoint.Y - endCenter.Y));

                Canvas.SetLeft(endCtrl, mousePoint.X - 5);
                Canvas.SetTop(endCtrl, mousePoint.Y - 5);

                Canvas.SetLeft(end, aptEnd.Top.X - 5);
                Canvas.SetTop(end, aptEnd.Top.Y - 5);

                endCtrlLine.X1 = mousePoint.X; endCtrlLine.Y1 = mousePoint.Y;
                endCtrlLine.X2 = aptEnd.Top.X; endCtrlLine.Y2 = aptEnd.Top.Y;

                //e.Handled = true;
            }
            else if (isDraggingTitle)
            {
                Point mousePoint = e.GetPosition(mainCanvas);
                Canvas.SetLeft(titleDraggingHandler, mousePoint.X - titleDraggingHandler.Width / 2);
                Canvas.SetTop(titleDraggingHandler, mousePoint.Y - titleDraggingHandler.Height / 2);
            }
            else if (isDraggingComment)
            {
                Comment curCmt = SelectedComment;
                if (curCmt == null) { isDraggingComment = false; return; }

                if (curCmt.IsSummary)
                {
                    //curCmt.RefreshLoaction();
                    return;
                }

                Point mousePoint = e.GetPosition(mainCanvas);

                Canvas.SetLeft(curCmt, mousePoint.X - commentDraggingSize.X);
                Canvas.SetTop(curCmt, mousePoint.Y - commentDraggingSize.Y);

                curCmt.RefreshConnectLine();
            }
            else if (isDraggingPictureBox)
            {
                PictureBox curPb = SelectedPictureBox;
                if (curPb == null) { isDraggingPictureBox = false; return; }

                Point mousePoint = e.GetPosition(mainCanvas);

                Canvas.SetLeft(curPb, mousePoint.X - pictureBoxDraggingSize.X);
                Canvas.SetTop(curPb, mousePoint.Y - pictureBoxDraggingSize.Y);

                curPb.RefreshConnectLine();
            }
            else if (isDraggingFixedTitle)
            {
                if (MainSelectedTitle == null) return;
                FixedTitle ft = MainSelectedTitle as FixedTitle;

                if (ft == null) { isDraggingFixedTitle = false; return; }

                Point mousePoint = e.GetPosition(mainCanvas);

                //Canvas.SetLeft(titleDraggingHandler, mousePoint.X - titleDraggingHandler.Width / 2);
                //Canvas.SetTop(titleDraggingHandler, mousePoint.Y - titleDraggingHandler.Height / 2);

                //Point tmpLeftTop = new Point(mousePoint.X + 4, mousePoint.Y - ft.ActualHeight / 2);

                Point tmpLeftTop = new Point(mousePoint.X - fixedTitleDraggingSize.X, mousePoint.Y - fixedTitleDraggingSize.Y);

                Canvas.SetLeft(ft, tmpLeftTop.X);
                Canvas.SetTop(ft, tmpLeftTop.Y);

                foreach (UIElement ue in mainCanvas.Children)
                {
                    Relation r = ue as Relation;
                    if (r != null)
                    {
                        Rect? tmpStartRect = null;
                        Rect? tmpEndRect = null;
                        if (r.StartTitleID == ft.ID)
                        {
                            tmpStartRect = new Rect(tmpLeftTop.X, tmpLeftTop.Y, ft.ActualWidth, ft.ActualHeight);
                        }

                        if (r.EndTitleID == ft.ID)
                        {
                            tmpEndRect = new Rect(tmpLeftTop.X, tmpLeftTop.Y, ft.ActualWidth, ft.ActualHeight);
                        }

                        r.RefreshLocation(tmpStartRect, tmpEndRect);
                    }
                }
            }
            else if (isResizeCommentWidth)
            {
                Comment comment = SelectedComment;
                if (comment == null)
                {
                    isResizeCommentWidth = false;
                    return;
                }

                Point leftTop = new Point(Canvas.GetLeft(comment), Canvas.GetTop(comment));

                Point newPt = e.GetPosition(mainCanvas);

                if (newPt.X - leftTop.X > 30)
                {
                    comment.MainTextPanel.Width = (newPt.X - leftTop.X - 20);
                    comment.RefreshConnectLine();
                }
                else
                {
                    comment.MainTextPanel.Width = 20;//最小宽20
                    comment.MainTextPanel.InvalidateArrange();
                    comment.MainTextPanel.UpdateLayout();
                    comment.RefreshTbxCaret();
                }

                if (comment.IsSummary)
                {
                    comment.RefreshSummary();
                }
            }
        }

        private void ControlEllipse_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            Relation.S_ContextMenu.IsOpen = true;
            e.Handled = true;
        }

        private void mainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isPrepareSnapshot)
            {
                selRect_MouseLeftButtonUp(sender, e);
                return;
            }

            if (isPrepareInsertCommentOrLinkTitle)
            {
                Commands.InsertCommentCommand.AddComment(e.GetPosition(mainCanvas));
                isPrepareInsertCommentOrLinkTitle = false;
            }
            else if (isPrepareInsertPictureBox)
            {
                Commands.InsertPictureBoxCommand.AddPictureBox(e.GetPosition(mainCanvas));
                isPrepareInsertPictureBox = false;
            }
            else if (isPrepareInsertFixedTitle)
            {
                Point basePoint = e.GetPosition(mainCanvas);
                Commands.InsertFixedTitleCommand.AddFixedTitle(
                    new Point(basePoint.X - 12, basePoint.Y - 12));
                isPrepareInsertFixedTitle = false;
            }

        }

        private void mainScrollViewer_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Comment cmt = SelectedComment;
            if (cmt != null)
            {
                if (isDraggingComment)
                {
                    Title masterTitle = GetTitleByID(cmt.MasterTitleID);
                    if (masterTitle == null)
                    {
                        isDraggingComment = false;
                        return;
                    }

                    Point leftTop = new Point(Canvas.GetLeft(cmt), Canvas.GetTop(cmt));

                    Point center = new Point(
                            masterTitle.LeftTop.X + masterTitle.ActualWidth / 2 +
                            (masterTitle.Direction == TitleDirection.Right ?
                            masterTitle.IndentWidth : -masterTitle.IndentWidth),
                            masterTitle.LeftTop.Y + masterTitle.ActualHeight / 2);

                    Point newLocation;

                    if (cmt.MasterTitle == cmt.Editor.RootTitle)
                    {
                        newLocation = new Point(Canvas.GetLeft(cmt), Canvas.GetTop(cmt));
                    }
                    else
                    {
                        newLocation = new Point(leftTop.X - center.X, leftTop.Y - center.Y);
                    }

                    if (newLocation.X >= cmt.Location.X - 5 && newLocation.X <= cmt.Location.X + 5 &&
                        newLocation.Y >= cmt.Location.Y - 5 && newLocation.Y <= cmt.Location.Y + 5)
                    {
                        cmt.RefreshLoaction();
                        isDraggingComment = false;
                        return;//没移动。
                    }

                    if (cmt.IsSummary == false)
                    {
                        ModifingItem mi = new ModifingItem() { ModifingName = "移动备注文本块位置" };

                        Action actLocation = new Action(cmt.ID, -1, cmt.GetType().Name, "Location", cmt.Location.ToString(),
                            newLocation.ToString());

                        cmt.Location = newLocation;

                        mi.Add(actLocation);

                        Do(mi);
                        IsModified = true;
                    }
                    else
                    {
                        cmt.RefreshLoaction();
                    }

                    isDraggingComment = false;
                    return;
                }
                else if (isResizeCommentWidth)
                {
                    Comment comment = SelectedComment;
                    if (comment == null)
                    {
                        isResizeCommentWidth = false;
                        return;
                    }

                    Point leftTop = new Point(Canvas.GetLeft(comment), Canvas.GetTop(comment));

                    Point newPt = e.GetPosition(mainCanvas);

                    double newWidth = 0;

                    if (newPt.X - leftTop.X > 30)
                    {
                        newWidth = (newPt.X - leftTop.X - 20);
                    }
                    else
                    {
                        newWidth = 20;//最小宽20
                    }

                    if (comment.TextBlockWidth != newWidth)
                    {
                        ModifingItem mi = new ModifingItem()
                        {
                            ModifingName = "更改备注文本块宽度",
                        };

                        Action act = new Action(comment.ID, -1, comment.GetType().Name, "TextBlockWidth",
                            comment.TextBlockWidth.ToString(), newWidth.ToString());

                        comment.TextBlockWidth = newWidth;

                        if (comment.IsSummary)
                        {
                            mi.IsNeedRefreshTitleAndLinesLocation = true;
                            RefreshTitleAndLinesLocation();
                        }
                        else
                        {
                            mi.IsNeedRefreshTitleAndLinesLocation = false;
                        }

                        mi.Add(act);

                        comment.RefreshConnectLine();
                        comment.Editor.Do(mi);
                        comment.Editor.IsModified = true;
                    }

                    isResizeCommentWidth = false;
                }
            }
            else
            {
                PictureBox pb = SelectedPictureBox;
                if (pb != null)
                {
                    if (isDraggingPictureBox == false) return;

                    Title masterTitle = GetTitleByID(pb.MasterTitleID);
                    if (masterTitle == null)
                    {
                        isDraggingPictureBox = false;
                        return;
                    }

                    Point leftTop = new Point(Canvas.GetLeft(pb), Canvas.GetTop(pb));

                    Point center = new Point(
                        masterTitle.LeftTop.X + masterTitle.ActualWidth / 2 +
                            (masterTitle.Direction == TitleDirection.Right ?
                            masterTitle.IndentWidth : -masterTitle.IndentWidth),
                        masterTitle.LeftTop.Y + masterTitle.ActualHeight / 2);

                    Point newLocation;

                    if (pb.MasterTitle == pb.Editor.RootTitle)
                    {
                        newLocation = new Point(Canvas.GetLeft(pb), Canvas.GetTop(pb));
                    }
                    else
                    {
                        newLocation = new Point(leftTop.X - center.X, leftTop.Y - center.Y);
                    }

                    if (newLocation.X >= pb.Location.X - 5 && newLocation.X <= pb.Location.X + 5 &&
                        newLocation.Y >= pb.Location.Y - 5 && newLocation.Y <= pb.Location.Y + 5)
                    {
                        pb.RefreshLoaction();
                        isDraggingPictureBox = false;
                        return;//没移动。
                    }

                    ModifingItem mi = new ModifingItem() { ModifingName = "移动图片框位置" };

                    Action actLocation = new Action(pb.ID, -1, pb.GetType().Name, "Location", pb.Location.ToString(),
                        newLocation.ToString());

                    pb.Location = newLocation;

                    mi.Add(actLocation);

                    Do(mi);
                    IsModified = true;

                    isDraggingPictureBox = false;
                    return;
                }
            }
        }

        private void rotateSlider_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (SelectedComment != null)
            {
                if (SelectedComment.RotateAngle != rotateSlider.Value)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转备注文本块" };

                    Action actRotate = new Action(SelectedComment.ID, -1, SelectedComment.GetType().Name,
                        "RotateAngle", SelectedComment.RotateAngle.ToString(), rotateSlider.Value.ToString());

                    SelectedComment.RotateAngle = rotateSlider.Value;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedPictureBox != null)
            {
                if (SelectedPictureBox.RotateAngle != rotateSlider.Value)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转图片框" };

                    Action actRotate = new Action(SelectedPictureBox.ID, -1, SelectedPictureBox.GetType().Name,
                        "RotateAngle", SelectedPictureBox.RotateAngle.ToString(), rotateSlider.Value.ToString());

                    SelectedPictureBox.RotateAngle = rotateSlider.Value;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedRelation != null)
            {
                if (SelectedRelation.RotateAngle != rotateSlider.Value)
                {
                    if (SelectedRelation.CommentText == null || SelectedRelation.CommentText.Length <= 0)
                    {
                        MessageBoxResult r = MessageBox.Show("关系线上没有设置备注文本，旋转也看到到效果。要继续吗？",
                            Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);

                        if (r != MessageBoxResult.Yes)
                        {
                            rotateSlider.Value = SelectedRelation.RotateAngle;
                            return;
                        }
                    }

                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转关系线备注文本" };

                    Action actRotate = new Action(SelectedRelation.ID, -1, SelectedRelation.GetType().Name,
                        "RotateAngle", SelectedRelation.RotateAngle.ToString(), rotateSlider.Value.ToString());

                    SelectedRelation.RotateAngle = rotateSlider.Value;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedTitleImage != null)
            {
                if (SelectedTitleImage.RotateAngle != rotateSlider.Value)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转标题图片" };

                    Action actRotate = new Action(SelectedTitleImage.ID, -1, SelectedTitleImage.GetType().Name,
                        "RotateAngle", SelectedTitleImage.RotateAngle.ToString(), rotateSlider.Value.ToString());

                    SelectedTitleImage.RotateAngle = rotateSlider.Value;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedTitlesList.Count > 0)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "旋转标题" };

                foreach (Title t in SelectedTitlesList)
                {
                    Action actRotate = new Action(t.ID, -1, t.GetType().Name,
                        "RotateAngle", t.RotateAngle.ToString(), rotateSlider.Value.ToString());

                    t.RotateAngle = rotateSlider.Value;

                    mi.Add(actRotate);
                }

                Do(mi);
                IsModified = true;
            }
            else
            {
                MessageBox.Show("请先选中某个要旋转的部件！", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                rotateSlider.Value = 0;
                return;
            }
        }

        private void rotateSlider_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            rotateSlider.Value = 0;

            if (SelectedComment != null)
            {
                if (SelectedComment.RotateAngle != 0)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转备注文本块[还原]" };

                    Action actRotate = new Action(SelectedComment.ID, -1, SelectedComment.GetType().Name,
                        "RotateAngle", SelectedComment.RotateAngle.ToString(), "0");

                    SelectedComment.RotateAngle = 0;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedRelation != null)
            {
                if (SelectedRelation.RotateAngle != 0)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转关系线备注文本[还原]" };

                    Action actRotate = new Action(SelectedRelation.ID, -1, SelectedRelation.GetType().Name,
                        "RotateAngle", SelectedRelation.RotateAngle.ToString(), "0");

                    SelectedRelation.RotateAngle = 0;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedTitleImage != null)
            {
                if (SelectedTitleImage.RotateAngle != 0)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "旋转标题图片[还原]" };

                    Action actRotate = new Action(SelectedTitleImage.ID, -1, SelectedTitleImage.GetType().Name,
                        "RotateAngle", SelectedTitleImage.RotateAngle.ToString(), "0");

                    SelectedTitleImage.RotateAngle = 0;

                    mi.Add(actRotate);

                    Do(mi);
                    IsModified = true;
                }
            }
            else if (SelectedTitlesList.Count > 0)
            {
                ModifingItem mi = new ModifingItem() { ModifingName = "旋转标题[还原]" };

                foreach (Title t in SelectedTitlesList)
                {
                    Action actRotate = new Action(t.ID, -1, t.GetType().Name,
                        "RotateAngle", t.RotateAngle.ToString(), "0");

                    t.RotateAngle = 0;

                    mi.Add(actRotate);
                }

                Do(mi);
                IsModified = true;
            }
            else
            {
                MessageBox.Show("请先选中某个要还原旋转角度的部件！", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
        }

        private void btnSwitchRightToolPadVisible_Click(object sender, RoutedEventArgs e)
        {
            if (rightToolGridColumn.Width.Value <= 10)
            {
                rightToolGridColumn.Width = new GridLength(330);
            }
            else
            {
                rightToolGridColumn.Width = new GridLength(0);
            }
        }

        private void btnSwitchLeftToolPadVisible_Click(object sender, RoutedEventArgs e)
        {
            if (leftToolGridColumn.Width.Value <= 10)
            {
                leftToolGridColumn.Width = new GridLength(330);
                TryLoadWorkspace();
            }
            else
            {
                leftToolGridColumn.Width = new GridLength(0);
            }
        }

        public void TryLoadWorkspace(bool anyway = false)
        {
            var workspacePath = Globals.PathOfWorkspace;
            if (System.IO.Directory.Exists(workspacePath))
            {
                if (System.IO.File.Exists(Globals.MainWindow.FullPathOfDiskFile))
                {
                    var wpath = workspacePath + "WorkspaceItems.xml";
                    if (System.IO.File.Exists(wpath))
                    {
                        var fi = new System.IO.FileInfo(wpath);
                        if (anyway == false)
                        {
                            if (fi.LastWriteTime.CompareTo(workspaceManager.PreLoadWorkspaceTime) > 0)
                            {
                                workspaceManager.LoadWorkspaceFromXml(wpath);
                            }
                        }
                        else
                        {
                            workspaceManager.LoadWorkspaceFromXml(wpath);
                        }
                    }
                }
            }
        }

        internal bool isDraggingTitle = false;

        private void titleDraggingHandler_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (MainSelectedTitle != null)
            {
                //if (MainSelectedTitle.IsFixed)//FixedTitle不显示了。2011年11月4日
                //{
                //    isDraggingFixedTitle = true;
                //    Point pt = e.GetPosition(this);
                //    SetFixedTitleDraggingSize(pt);
                //    e.Handled = true;
                //}
                //else
                //{
                isDraggingTitle = true;
                e.Handled = true;
                //}
            }
        }

        private void titleDraggingHandler_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (MainSelectedTitle != null)
            {
                //FixedTitle不显示了。2011年11月4日已移动到FixedTitle类中。

                isDraggingTitle = false;

                Title mainT = null;
                foreach (Title t in SelectedTitlesList)
                {
                    if (t.IsMainSelectedTitle)
                    {
                        mainT = t; break;
                    }
                }

                if (mainT != null)
                {
                    mainT.RefreshEditorTitleDraggingHandler();
                }
            }
            //e.Handled = true;//不然Title接收不到。
        }

        private void mainScrollViewer_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //这里的所有相关变量都是在mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);中处理的。
            //isDraggingTitle不是在这个事件处理器中处理，因此本方法中不涉及。

            if (isDraggingComment)
            {
                mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);
                //isDraggingComment = false;//mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);会处理
                e.Handled = true;
                return;
            }

            if (isDraggingEndCtrl)
            {
                mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);
                //isDraggingEndCtrl = false;//mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);会处理
                e.Handled = true;
                return;
            }

            if (isDraggingPictureBox)
            {
                mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);
                //isDraggingPictureBox = false;//mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);会处理
                e.Handled = true;
                return;
            }

            if (isDraggingStartCtrl)
            {
                mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);
                //isDraggingStartCtrl = false;//mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);会处理
                e.Handled = true;
                return;
            }

            if (isResizeCommentWidth)
            {
                mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);
                //isResizeCommentWidth = false;//mainScrollViewer_PreviewMouseLeftButtonUp(sender, e);会处理
                e.Handled = true;
                return;
            }

        }

        private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            bool isShift = false;

            KeyStates lShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates rShift = Keyboard.GetKeyStates(Key.RightShift);

            bool isCtrl = false;

            if ((lShift & KeyStates.Down) > 0 || (rShift & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            KeyStates lCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates rCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            if ((lCtrl & KeyStates.Down) > 0 || (rCtrl & KeyStates.Down) > 0)
            {
                isCtrl = true;
            }

            //这些用于弹出右键菜单
            if (e.Key == Key.Apps || (e.Key == Key.System && e.SystemKey == Key.F10))
            {
                if (this.SelectedTitlesList.Count > 0)
                {
                    LunarMind.Title.ShowContextMenu();
                }

                if (this.selectedPictureBox != null)
                {
                    LunarMind.PictureBox.ShowContextMenu();
                }

                if (this.selectedComment != null)
                {
                    LunarMind.Comment.ShowContextMenu();
                }

                if (this.selectedRelation != null)
                {
                    LunarMind.Relation.ShowContextMenu();
                }

                if (this.selectedTitleImage != null)
                {
                    this.selectedTitleImage.ContextMenu.IsOpen = true; //没有把它做成静态的。

                }

                //不加这行会导致Key.Apps在KeyUp事件中立即将快捷菜单再隐藏掉。
                e.Handled = true;
            }

            //这是用于设置标题的Indent
            if (e.Key < Key.D0 || e.Key > Key.D9) return;
            if (isShift || isCtrl == false) return;

            SetTitleIndentCommand.SetTitleIndent((int)(e.Key - Key.D0));

        }

        private void miAddRelation_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miAddRelation.IsEnabled)
                imgUnableMiAddRelation.Visibility = Visibility.Hidden;
            else
                imgUnableMiAddRelation.Visibility = Visibility.Visible;
        }

        private void miSave_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miSave.IsEnabled) imgUnableMiSave.Visibility = Visibility.Hidden;
            else imgUnableMiSave.Visibility = Visibility.Visible;
        }

        private void miUndo_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miUndo.IsEnabled) imgUnableMiUndo.Visibility = Visibility.Hidden;
            else imgUnableMiUndo.Visibility = Visibility.Visible;
        }

        private void miRedo_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miRedo.IsEnabled) imgUnableMiRedo.Visibility = Visibility.Hidden;
            else imgUnableMiRedo.Visibility = Visibility.Visible;
        }

        private void miCut_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miCut.IsEnabled) imgUnableMiCut.Visibility = Visibility.Hidden;
            else imgUnableMiCut.Visibility = Visibility.Visible;
        }

        private void miCopy_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miCopy.IsEnabled) imgUnableMiCopy.Visibility = Visibility.Hidden;
            else imgUnableMiCopy.Visibility = Visibility.Visible;
        }

        private void miPaste_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miPaste.IsEnabled) imgUnableMiPaste.Visibility = Visibility.Hidden;
            else imgUnableMiPaste.Visibility = Visibility.Visible;
        }

        private void miDelete_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miDelete.IsEnabled) imgUnableMiDelete.Visibility = Visibility.Hidden;
            else imgUnableMiDelete.Visibility = Visibility.Visible;
        }

        private void miInsertSameLevelTitle_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miInsertSameLevelTitle.IsEnabled) imgUnableMiInsertSameLevelTitle.Visibility = Visibility.Hidden;
            else imgUnableMiInsertSameLevelTitle.Visibility = Visibility.Visible;
        }

        private void miInsertLowerLevelTitle_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (miInsertLowerLevelTitle.IsEnabled) imgUnableMiInsertLowerLevelTitle.Visibility = Visibility.Hidden;
            else imgUnableMiInsertLowerLevelTitle.Visibility = Visibility.Visible;
        }

        private void miDefaultMindMapView_Click(object sender, RoutedEventArgs e)
        {
            if (this.View == ViewMode.Default) return;

            ModifingItem mi = new ModifingItem()
            {
                ModifingName = "更改视图模式",
                IsNeedRefreshTitleAndLinesLocation = true
            };

            Action act = new Action(null, -1, null, "ViewMode", this.View.ToString(),
                ViewMode.Default.ToString(), ActionType.ViewMode, null);

            this.View = ViewMode.Default;
            this.RefreshView();

            mi.Add(act);
            this.IsModified = true;
            this.Do(mi);
        }

        private void miHorizontalListView_Click(object sender, RoutedEventArgs e)
        {
            if (this.View == ViewMode.HorizontalList) return;

            ModifingItem mi = new ModifingItem()
            {
                ModifingName = "更改视图模式",
                IsNeedRefreshTitleAndLinesLocation = true
            };

            Action act = new Action(null, -1, null, "ViewMode", this.View.ToString(),
                ViewMode.HorizontalList.ToString(), ActionType.ViewMode, null);

            this.View = ViewMode.HorizontalList;
            this.RefreshView();

            mi.Add(act);
            this.IsModified = true;
            this.Do(mi);
        }

        private void miVerticalListView_Click(object sender, RoutedEventArgs e)
        {
            if (this.View == ViewMode.VerticalList) return;

            ModifingItem mi = new ModifingItem()
            {
                ModifingName = "更改视图模式",
                IsNeedRefreshTitleAndLinesLocation = true
            };

            Action act = new Action(null, -1, null, "ViewMode", this.View.ToString(),
                ViewMode.VerticalList.ToString(), ActionType.ViewMode, null);

            this.View = ViewMode.VerticalList;
            this.RefreshView();

            mi.Add(act);
            this.IsModified = true;
            this.Do(mi);
        }

        private void miPageRulerHorizontal_Click(object sender, RoutedEventArgs e)
        {
            pageDirection = Orientation.Horizontal;
            RefreshPageAdorner();
        }

        private void miPageRulerVertical_Click(object sender, RoutedEventArgs e)
        {
            pageDirection = Orientation.Vertical;
            RefreshPageAdorner();
        }

        private void miPageRulerA4_Click(object sender, RoutedEventArgs e)
        {
            PageSetting = PaperInfo.GetPaperInfo("A4");
            RefreshPageAdorner();
        }

        private void miPageRulerA3_Click(object sender, RoutedEventArgs e)
        {
            PageSetting = PaperInfo.GetPaperInfo("A3");
            RefreshPageAdorner();
        }

        public bool isPageRulerVisible = false;

        private void miShowPageRuler_Click(object sender, RoutedEventArgs e)
        {
            isPageRulerVisible = true;
            RefreshPageAdorner();
        }

        private void mView_SubmenuOpened(object sender, RoutedEventArgs e)
        {
            Image imgPage = new Image();
            imgPage.Width = imgPage.Height = 16;
            imgPage.Source = new BitmapImage(
                new Uri("pack://Application:,,,/LunarMind;component/Resources/Icons/Selected.png"));

            //暂时不再支持标尺。2012年5月24日
            //if (pageSetting.PaperTypeName == "A4")
            //{
            //    miPageRulerA4.Icon = imgPage;
            //    miPageRulerA3.Icon = null;
            //}
            //else if (pageSetting.PaperTypeName == "A3")
            //{
            //    miPageRulerA4.Icon = null;
            //    miPageRulerA3.Icon = imgPage;
            //}
            //else
            //{
            //    miPageRulerA4.Icon = null;
            //    miPageRulerA3.Icon = null;
            //}

            Image imgDirection = new Image();
            imgDirection.Width = imgDirection.Height = 16;
            imgDirection.Source = new BitmapImage(
                new Uri("pack://Application:,,,/LunarMind;component/Resources/Icons/Selected.png"));

            //暂时不再支持标尺。因为添加了打印到A4纸这个功能。二○一二年五月二十四日
            //if (pageDirection == Orientation.Horizontal)
            //{
            //    miPageRulerHorizontal.Icon = imgDirection;
            //    miPageRulerVertical.Icon = null;
            //}
            //else
            //{
            //    miPageRulerHorizontal.Icon = null;
            //    miPageRulerVertical.Icon = imgDirection;
            //}

            switch (view)
            {
                case ViewMode.HorizontalList:
                    {
                        miDefaultMindMapView.IsChecked = false;
                        miVerticalListView.IsChecked = false;
                        miHorizontalListView.IsChecked = true;
                        break;
                    }
                case ViewMode.VerticalList:
                    {
                        miDefaultMindMapView.IsChecked = false;
                        miVerticalListView.IsChecked = true;
                        miHorizontalListView.IsChecked = false;
                        break;
                    }
                default:
                    {
                        miDefaultMindMapView.IsChecked = true;
                        miVerticalListView.IsChecked = false;
                        miHorizontalListView.IsChecked = false;
                        break;
                    }
            }
        }

        private void miRecentFiles_Click(object sender, RoutedEventArgs e)
        {
            if (tcRightTools.SelectedItem != tiRecentFiles)
            {
                tcRightTools.SelectedItem = tiRecentFiles;
            }

            if (rightToolGridColumn.Width.Value <= 10)
            {
                rightToolGridColumn.Width = new GridLength(330);
            }
        }

        /// <summary>
        /// 用于决定鼠标右键拖动时卷动的起点。
        /// </summary>
        private Point scrollStart = new Point(0, 0);

        private Point scrollStartOffcet = new Point(0, 0);

        private bool isRightButtonDragging = false;

        private void mainScrollViewer_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            isRightButtonDragging = true;

            scrollStart = e.GetPosition(mainScrollViewer);
            scrollStartOffcet = new Point(mainScrollViewer.HorizontalOffset, mainScrollViewer.VerticalOffset);
            e.Handled = true;
        }

        private void mainScrollViewer_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            isRightButtonDragging = false;
            e.Handled = true;
        }

        private void mainScrollViewer_MouseMove(object sender, MouseEventArgs e)
        {
            if (isRightButtonDragging == false) return;

            Point endPoint = e.GetPosition(mainScrollViewer);

            mainScrollViewer.ScrollToHorizontalOffset(scrollStart.X - endPoint.X + scrollStartOffcet.X);
            mainScrollViewer.ScrollToVerticalOffset(scrollStart.Y - endPoint.Y + scrollStartOffcet.Y);
            e.Handled = true;
        }

        private void mainScrollViewer_MouseLeave(object sender, MouseEventArgs e)
        {
            isRightButtonDragging = false;
            e.Handled = true;
        }

        private void sliderOfCanvasScale_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (sliderOfCanvasScale.Value == 0)//唯有100%缩放情况下按“锐利”显示。
            {
                TextOptions.SetTextFormattingMode(mainCanvas, TextFormattingMode.Display);
            }
            else
            {
                TextOptions.SetTextFormattingMode(mainCanvas, TextFormattingMode.Ideal);
            }

            RefreshMousePoint();
        }

        private void miZoomIn_Click(object sender, RoutedEventArgs e)
        {
            if (sliderOfCanvasScale.Value + 1 <= sliderOfCanvasScale.Maximum)
            {
                sliderOfCanvasScale.Value += 1;
            }
        }

        private void miZoomOut_Click(object sender, RoutedEventArgs e)
        {
            if (sliderOfCanvasScale.Value - 1 >= sliderOfCanvasScale.Minimum)
            {
                sliderOfCanvasScale.Value -= 1;
            }
        }

        private void miZoomCancel_Click(object sender, RoutedEventArgs e)
        {
            sliderOfCanvasScale.Value = 0;
        }

        private void mainCanvas_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (isPrepareSnapshot && (e.LeftButton & MouseButtonState.Pressed) > 0)
            {
                selPointB = e.GetPosition(mainCanvas);

                Rect snapShortRect = SnaptShortRect;
                Canvas.SetLeft(selRectangle, snapShortRect.Left);
                Canvas.SetTop(selRectangle, snapShortRect.Top);

                selRectangle.Width = snapShortRect.Width;
                selRectangle.Height = snapShortRect.Height;

                this.selectedBoxFillBrush.Viewbox = new Rect(snapShortRect.X - 2, snapShortRect.Y - 2, snapShortRect.Width + 4, snapShortRect.Height + 4);

                if (selRectangle.Visibility != Visibility) selRectangle.Visibility = System.Windows.Visibility.Visible;
                return;
            }
        }

        /// <summary>
        /// 用于截图的VisualBrush
        /// </summary>
        private VisualBrush selectedBoxFillBrush = new VisualBrush()
        {
            ViewboxUnits = BrushMappingMode.Absolute,
            Stretch = Stretch.None,
        };

        public VisualBrush SelectedBoxFillBrush
        {
            get { return this.selectedBoxFillBrush; }
        }

        private void tbSnapshot_Click(object sender, RoutedEventArgs e)
        {
            if (tbSnapshot.IsChecked == true)
            {
                isPrepareSnapshot = true;
            }
            else isPrepareSnapshot = false;

            if (isPrepareSnapshot == false)
            {
                selRectangle.Visibility = System.Windows.Visibility.Collapsed;
            }
            else
            {
                if (mainCanvas.Children.Contains(selRectangle) == false)
                {
                    mainCanvas.Children.Add(selRectangle);
                }
            }
        }

        private void selRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            //截图
            CopySelectedAreaAsImageWindow newWindow = new CopySelectedAreaAsImageWindow()
            {
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Owner = this,
            };

            newWindow.MainRect.Fill = this.selectedBoxFillBrush;

            newWindow.MainRect.Width = newWindow.MainCanvas.Width = selRectangle.Width - 3;
            newWindow.MainRect.Height = newWindow.MainCanvas.Height = selRectangle.Height - 3;
            newWindow.ShowDialog();

            //截图结束
            isPrepareInsertCommentOrLinkTitle =
                isPrepareInsertFixedTitle =
                isPrepareInsertPictureBox =
                isPrepareSnapshot = false;

            //复位虚框
            tbSnapshot.IsChecked = false;
            selPointA = selPointB = new Point(-1, -1);
            selRectangle.Visibility = Visibility.Collapsed;

        }

        #region 此处实现屏幕扩展功能

        private VisualBrush slideVisualBrush;

        private SlideWindow slideWindow;

        #endregion

        private void miExpandPresentation_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.Screen[] screens;
            screens = System.Windows.Forms.Screen.AllScreens; //get all the screen width and heights 
            if (screens.Length < 2)
            {
                MessageBox.Show("　　此功能只在“扩展”模式下才有效！这需要更改操作系统桌面属性（在Windows 7中，可以按Windows键+P键来快速更改桌面扩展还是复制）。",
                    "Lunar Mind", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (miExpandPresentation.IsChecked == true)
            {
                miExpandPresentation.IsChecked = false;
            }
            else miExpandPresentation.IsChecked = true;

            RefreshPresentation(screens);
        }

        private void RefreshPresentation(System.Windows.Forms.Screen[] screens)
        {
            if (miExpandPresentation.IsChecked == true)
            {
                this.slideAdorner.Visibility = System.Windows.Visibility.Visible;

                if (this.slideVisualBrush == null)
                {
                    this.slideVisualBrush = new VisualBrush()
                    {
                        Stretch = Stretch.None,
                    };
                }

                this.slideVisualBrush.AlignmentX = AlignmentX.Left;
                this.slideVisualBrush.AlignmentY = AlignmentY.Top;

                double leftOffset, topOffset;

                if (this.mainScrollViewer.ComputedVerticalScrollBarVisibility == System.Windows.Visibility.Visible)
                {
                    leftOffset = (this.mainScrollViewer.ActualWidth - SystemParameters.VerticalScrollBarWidth - screens[1].WorkingArea.Width) / 2;
                }
                else
                {
                    leftOffset = (this.mainScrollViewer.ActualWidth - screens[1].WorkingArea.Width) / 2;
                }

                if (this.mainScrollViewer.ComputedHorizontalScrollBarVisibility == System.Windows.Visibility.Visible)
                {
                    topOffset = (this.mainScrollViewer.ActualHeight - SystemParameters.HorizontalScrollBarHeight - screens[1].WorkingArea.Height) / 2;
                }
                else
                {
                    topOffset = (this.mainScrollViewer.ActualHeight - screens[1].WorkingArea.Height) / 2;
                }

                this.slideVisualBrush.ViewportUnits = BrushMappingMode.Absolute;
                this.slideVisualBrush.Viewport = new Rect(-leftOffset, -topOffset,
                    screens[1].WorkingArea.Width + leftOffset, screens[1].WorkingArea.Height + topOffset);
                this.slideVisualBrush.Visual = this.mainScrollViewer;

                if (this.slideWindow == null)
                {
                    this.slideWindow = new SlideWindow() { Owner = this, SourceVisualBrush = this.slideVisualBrush, };
                }

                if (this.slideWindow != null)
                {
                    this.slideWindow.Left = screens[1].Bounds.Left;
                    this.slideWindow.Top = screens[1].Bounds.Top;
                    this.slideWindow.Width = screens[1].WorkingArea.Width;
                    this.slideWindow.Height = screens[1].WorkingArea.Height;
                    this.slideWindow.WindowStartupLocation = System.Windows.WindowStartupLocation.Manual;
                    this.slideWindow.Show();

                    //this.slideWindow.Width = 640;
                    //this.slideWindow.Height = 480;
                    //this.slideWindow.Show();
                }
            }
            else// if (miExpandPresentation.IsChecked == false)
            {
                //结束分屏演示
                this.slideAdorner.Visibility = System.Windows.Visibility.Collapsed;

                if (this.slideVisualBrush != null)
                {
                    this.slideVisualBrush = null;
                }

                if (this.slideWindow != null)
                {
                    this.slideWindow.Close();
                    this.slideWindow = null;
                }
            }
        }

        private void mainScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            var screens = System.Windows.Forms.Screen.AllScreens; //get all the screen width and heights 
            if (screens.Length >= 2)
            {
                RefreshPresentation(screens);
            }

            RefreshMousePoint();
        }

        public void RefreshMousePoint()
        {
            if (tbtnFullScreen.IsChecked == true)
            {
                if (this.MainSelectedTitle != null)
                {
                    RectangleGeometryA.Rect = new Rect(0, 0, this.baseCanvas.Width, this.baseCanvas.Height);

                    if (this.MainSelectedTitle.IsRootTitle)
                    {
                        RectangleGeometryB.Rect = new Rect(
                            this.MainSelectedTitle.LeftTop.X + this.MainSelectedTitle.IndentWidth,
                            this.MainSelectedTitle.LeftTop.Y,
                            this.MainSelectedTitle.ActualWidth,
                            this.MainSelectedTitle.ActualHeight
                            );
                    }
                    else
                    {
                        RectangleGeometryB.Rect = new Rect(
                            this.MainSelectedTitle.LeftTop.X + this.MainSelectedTitle.IndentWidth - 12,
                            this.MainSelectedTitle.LeftTop.Y,
                            this.MainSelectedTitle.ActualWidth + 24,
                            this.MainSelectedTitle.ActualHeight
                            );
                    }
                    PresentationShadow.Visibility = System.Windows.Visibility.Visible;
                }
                else
                {
                    PresentationShadow.Visibility = System.Windows.Visibility.Collapsed;
                }
            }
            else
            {
                PresentationShadow.Visibility = System.Windows.Visibility.Collapsed;
            }

            //刷新鼠标指针位置标记。
            this.mousePointer.Visibility = System.Windows.Visibility.Hidden;

            for (int i = 0; i < this.mainCanvas.Children.Count; i++)
            {
                UIElement ue = this.mainCanvas.Children[i];
                var t = ue as Title;
                if (t == null) continue;

                if (t.IsMouseOver)
                {
                    RefreshMousePointToTitle(t);
                }
            }
        }

        public void RefreshMousePointToTitle(Title title)
        {
            //自定位置，不受布局影响的标题，不显示鼠标指针位置标记。
            //否则还需要处理“拖动自定义（自由）标题”时的标记跟随，太麻烦又无必要。
            if (title == null || title.IsFixed || title.IsMainSelectedTitle)
            {
                this.mousePointer.Visibility = System.Windows.Visibility.Hidden;
                return;
            }

            if (this.mainCanvas.Children.Contains(this.mousePointer) == false)
            {
                this.mainCanvas.Children.Add(Globals.MainWindow.mousePointer);
            }

            Rect rect = new Rect(title.LeftTop, title.RenderSize);

            //更改宽、高
            double h = rect.Height * 3;
            if (h > 0)
            {
                double r = h / 128;

                Globals.MainWindow.preSelScaleTransform.ScaleX =
                Globals.MainWindow.preSelScaleTransform.ScaleY = r;
            }
            else
            {
                Globals.MainWindow.preSelScaleTransform.ScaleX =
                Globals.MainWindow.preSelScaleTransform.ScaleY = 1;
            }

            Canvas.SetTop(Globals.MainWindow.mousePointer, title.LeftTop.Y + rect.Height / 2 - 69);
            Canvas.SetLeft(Globals.MainWindow.mousePointer, title.LeftTop.X + rect.Width / 2 - 69);

            //将用于标志鼠标位置的齿轮标记置于最底层。
            Canvas.SetZIndex(mousePointer, int.MinValue);

            Globals.MainWindow.mousePointer.Visibility = System.Windows.Visibility.Visible;
        }


        public void RefreshMainSelectedTitle(Title title)
        {
            //自定位置，不受布局影响的标题，不显示鼠标指针位置标记。
            //否则还需要处理“拖动自定义（自由）标题”时的标记跟随，太麻烦又无必要。
            if (title == null || title.IsFixed)
            {
                this.selectedTitlePointer.Visibility = System.Windows.Visibility.Hidden;
                return;
            }

            if (this.mainCanvas.Children.Contains(this.selectedTitlePointer) == false)
            {
                this.mainCanvas.Children.Add(Globals.MainWindow.selectedTitlePointer);
            }

            Rect rect = new Rect(title.LeftTop, title.RenderSize);

            //更改宽、高
            double h = rect.Height * 3;
            if (h > 0)
            {
                double r = h / 128;

                Globals.MainWindow.preSelScaleTransformOfSelectedTitle.ScaleX =
                Globals.MainWindow.preSelScaleTransformOfSelectedTitle.ScaleY = r;
            }
            else
            {
                Globals.MainWindow.preSelScaleTransformOfSelectedTitle.ScaleX =
                Globals.MainWindow.preSelScaleTransformOfSelectedTitle.ScaleY = 1;
            }

            Canvas.SetTop(Globals.MainWindow.selectedTitlePointer, title.LeftTop.Y + rect.Height / 2 - 69);
            Canvas.SetLeft(Globals.MainWindow.selectedTitlePointer, title.LeftTop.X + rect.Width / 2 - 69);

            //将用于标志鼠标位置的齿轮标记置于最底层。
            Canvas.SetZIndex(selectedTitlePointer, int.MinValue);

            Globals.MainWindow.selectedTitlePointer.Visibility = System.Windows.Visibility.Visible;
        }

        private void mainCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.baseCanvas.Width = mainCanvas.Width;
            this.baseCanvas.Height = mainCanvas.Height;

            RefreshMousePoint();
        }

        /// <summary>
        /// Create a new animation to the x,y offset.  I beleive you need the have .NET 3.5 SP1 installed for
        /// this to work.  If you don't you can't call Storyboard.SetTarget.
        /// </summary>
        /// <param name="x">X position</param>
        /// <param name="y">Y position</param>
        private void ScrollToPositionByAnimation(double x, double y, bool ignoreX = false, bool ignoreY = false)
        {
            Storyboard sb = new Storyboard();

            if (ignoreY == false)
            {
                DoubleAnimation vertAnim = new DoubleAnimation();
                vertAnim.From = mainScrollViewer.VerticalOffset;
                vertAnim.To = y;
                vertAnim.DecelerationRatio = .2;
                vertAnim.Duration = new Duration(TimeSpan.FromMilliseconds(1000));//1000毫秒
                sb.Children.Add(vertAnim);
                Storyboard.SetTarget(vertAnim, mainScrollViewer);
                Storyboard.SetTargetProperty(vertAnim, new PropertyPath(AniScrollViewer.CurrentVerticalOffsetProperty));
            }

            if (ignoreX == false)
            {
                DoubleAnimation horzAnim = new DoubleAnimation();
                horzAnim.From = mainScrollViewer.HorizontalOffset;
                horzAnim.To = x;
                horzAnim.DecelerationRatio = .2;
                horzAnim.Duration = new Duration(TimeSpan.FromMilliseconds(1000));

                sb.Children.Add(horzAnim);
                Storyboard.SetTarget(horzAnim, mainScrollViewer);
                Storyboard.SetTargetProperty(horzAnim, new PropertyPath(AniScrollViewer.CurrentHorizontalOffsetProperty));
            }

            sb.Begin();
        }


        /// <summary>
        /// 尝试在可视区域显示指定标题。
        /// 此方法用于用于演示。而Title.TryToShowTitleInVisualArea(Title title, bool moreBottomBlank = false)方法用于编辑时尝试显示正在编辑的标题，避免被编辑窗口（在主窗口底侧位置）遮挡。
        /// </summary>
        /// <param name="title"></param>
        /// <param name="scrollAnyWhere">无论如何都滚动。此值为默认值（false）时，
        /// 如果title已经完全在可视区中时，不会滚动，避免眼睛疲劳。</param>
        public void TryScrollTitle(Title title, bool scrollAnyWhere = false)
        {
            //改用动画来解决自动移动呈现标题的问题。
            //Get the position of the button located in the tag
            //if (title != null)
            //{
            //    Point relativePoint = title.TransformToAncestor(mainContent).Transform(new Point(0, 0));
            //    ScrollToPosition(relativePoint.X, relativePoint.Y);
            //}

            #region 二○一三年十二月二十日 改动画效果移动之前的代码。
            if (title == null || this.mainCanvas.Children.Contains(title) == false) return;


            //if (title == RootTitle) { TryToScrollRootTitleAtCenter(); return; }
            //现在TryToScrollRootTitleAtCenter()这个方法是通过调用本方法来完成的。

            Point basePt = new Point(0, 0);

            //这两句不能少，否则，因为异步的关系，可能取不到正确的点坐标。
            title.InvalidateArrange();
            title.UpdateLayout();

            bool vVisible = false;
            bool hVisible = false;

            if (scrollAnyWhere == false)
            {
                //如果title已经可见（完全在可视区域显示），则不滚动，直接返回。
                Point lt = title.TranslatePoint(basePt, mainScrollViewer);
                Point rb = new Point(lt.X + title.ActualWidth * scaleTransForm.ScaleX, lt.Y + title.ActualHeight * scaleTransForm.ScaleY);

                if (askPanel.Visibility == System.Windows.Visibility.Visible)
                {
                    if (lt.X >= 0 && rb.X <= mainScrollViewer.ViewportWidth) hVisible = true;

                    if (lt.Y >= 0 && rb.Y <= mainScrollViewer.ViewportHeight - askPanel.ActualHeight) vVisible = true;

                    if (hVisible && vVisible) return;
                }
                else
                {
                    if (lt.X >= 0 && rb.X <= mainScrollViewer.ViewportWidth) hVisible = true;

                    if (lt.Y >= 0 && rb.Y <= mainScrollViewer.ViewportHeight) vVisible = true;

                    if (hVisible && vVisible) return;
                }
            }

            Point leftTop = title.TranslatePoint(basePt, mainCanvas);

            leftTop.X += outBorder.Margin.Left;
            leftTop.Y += outBorder.Margin.Top;

            leftTop.X *= scaleTransForm.ScaleX;//缩放
            leftTop.Y *= scaleTransForm.ScaleY;

            double vOffset;
            double hOffset;

            switch (View)
            {
                case ViewMode.HorizontalList://2014年3月28日，目前只有三种视图。
                case ViewMode.VerticalList:  //这两种一样。
                    {
                        var blankAreaWidth = Globals.BlankAreaWidth;
                        vOffset = leftTop.Y + title.ActualHeight * scaleTransForm.ScaleY +
                            blankAreaWidth * scaleTransForm.ScaleY - mainScrollViewer.ViewportHeight;
                        hOffset = leftTop.X - title.LevelIndex * blankAreaWidth;
                        break;
                    }
                //case ViewMode.Default:
                default:
                    {
                        vOffset = leftTop.Y - (mainScrollViewer.ViewportHeight / 2 - title.ActualHeight * scaleTransForm.ScaleY / 2);
                        hOffset = leftTop.X - (mainScrollViewer.ViewportWidth / 2 - title.ActualWidth * scaleTransForm.ScaleX / 2);
                        break;
                    }
            }

            //全屏演示时再考虑垂直居中好了
            if (tbtnFullScreen.IsChecked == true)
            {
                if (askPanel.Visibility == System.Windows.Visibility.Visible)
                {
                    vOffset += askPanel.ActualHeight;
                }
                else
                {
                    vOffset += (mainScrollViewer.ActualHeight / 2 - title.ActualHeight * scaleTransForm.ScaleY / 2);
                }
            }

            //下面这串代码会出错。
            //if (title.Direction == TitleDirection.Right)
            //{
            //    hOffset = leftTop.X - 10;

            //    var masterTitle = title.GetHigherLvelTitle();
            //    if (masterTitle != null)
            //    {
            //        var masterTitleLeftTop = masterTitle.TranslatePoint(basePt, mainCanvas);
            //        masterTitleLeftTop.X += outBorder.Margin.Left;
            //        masterTitleLeftTop.Y += outBorder.Margin.Top;
            //        hOffset = masterTitleLeftTop.X - 10;
            //    }
            //}
            //else
            //{
            //    hOffset = leftTop.X - 10 - (mainScrollViewer.ViewportWidth - title.ActualWidth);

            //    var masterTitle = title.GetHigherLvelTitle();
            //    if (masterTitle != null)
            //    {
            //        var masterTitleLeftTop = masterTitle.TranslatePoint(basePt, mainCanvas);
            //        masterTitleLeftTop.X += outBorder.Margin.Left;
            //        masterTitleLeftTop.Y += outBorder.Margin.Top;
            //        hOffset = masterTitleLeftTop.X + 30 - (mainScrollViewer.ViewportWidth - masterTitle.ActualWidth);
            //    }
            //}

            if (tbtnFullScreen.IsChecked == true || ckxAutoPresentation.IsChecked == true)
            {
                //如果横向在可视区，则横坐标不动。
                //如果纵向在可视区，则纵坐标不动。

                ScrollToPositionByAnimation(hOffset, vOffset, hVisible ? true : false, vVisible ? true : false);
            }
            else
            {
                //在编辑时，如果用动画滚动会使人恶心欲吐。2014年1月12日
                mainScrollViewer.ScrollToHorizontalOffset(hOffset);
                mainScrollViewer.ScrollToVerticalOffset(vOffset);
            }
            #endregion
        }

        private WindowState restoreWindowState = WindowState.Normal;

        private void tbtnFullScreen_Click(object sender, RoutedEventArgs e)
        {
            if (tbtnFullScreen.IsChecked == true)
            {
                //不需要，选定Title时会自动出现。
                //PresentationShadow.Visibility = System.Windows.Visibility.Collapsed;

                restoreWindowState = this.WindowState;

                statusLine.Height =
                toolBarLine.Height =
                menuLine.Height = new GridLength(0);

                rightToolGridColumn.Width = new GridLength(0);

                this.WindowStyle = System.Windows.WindowStyle.None;

                //this.Topmost = true;

                this.WindowState = System.Windows.WindowState.Maximized;


                this.tbFullScreenTooltip.Visibility =
                this.btnExitFullScreen.Visibility = System.Windows.Visibility.Visible;

                this.AutoAskTip.Visibility = miAutoAskForFillBlanks.IsChecked ? Visibility.Visible : Visibility.Collapsed;

                //隐藏所有Title的填空项
                foreach (UIElement ue in mainCanvas.Children)
                {
                    var title = ue as Title;
                    if (title == null) continue;

                    title.IsFillBlankVisible = false;
                    title.ImagesPresentated = false;
                }
                this.RefreshTitleAndLinesLocation();

                Globals.SwitchInputMethod(false);

                if (this.MainSelectedTitle == null)
                {
                    if (this.RootTitle != null)
                    {
                        this.RootTitle.SelectOnlySelf();
                        TryToScrollRootTitleAtCenter();
                    }
                }
                else
                {
                    TryScrollTitle(this.MainSelectedTitle);
                }
            }
            else
            {
                PresentationShadow.Visibility = System.Windows.Visibility.Collapsed;

                menuLine.Height = new GridLength(20);
                toolBarLine.Height = new GridLength(0, GridUnitType.Auto);
                statusLine.Height = new GridLength(32);

                this.WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
                //this.Topmost = false;

                this.WindowState = restoreWindowState;

                this.tbFullScreenTooltip.Visibility =
                this.AutoAskTip.Visibility =
                this.btnExitFullScreen.Visibility = System.Windows.Visibility.Collapsed;

                bool fillBlankVisible = !(ckxForceFillBlankView.IsChecked == true);

                //显示所有Title的填空项。2013年10月6日改为显示填空项文本。令标题直接支持“填空模式”。
                foreach (UIElement ue in mainCanvas.Children)
                {
                    var title = ue as Title;
                    if (title == null) continue;

                    title.IsFillBlankVisible = fillBlankVisible;
                    //2014年1月28日在主界面添加“强制显示为填空模式”选取框。
                }

                this.RefreshTitleAndLinesLocation();

                Globals.SwitchInputMethod(true);

                if (this.MainSelectedTitle == null)
                {
                    if (this.RootTitle != null)
                    {
                        this.RootTitle.SelectOnlySelf();
                        TryToScrollRootTitleAtCenter();
                    }
                }
                else
                {
                    TryScrollTitle(this.MainSelectedTitle);
                }

                askPanel.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        /// <summary>
        /// 用于自动演示的定时器。
        /// </summary>
        private System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer()
        {
            Interval = new TimeSpan(0, 0, 4)
        };

        private void ckxAutoPresentation_Click(object sender, RoutedEventArgs e)
        {
            if (tbtnFullScreen.IsChecked != false) return;
            //全屏演示状态下，暂时不支持自动演示，还没有想好要不要这样做。

            if (ckxAutoPresentation.IsChecked == true)
            {
                timer.Start();

                //在普通视图下，如果没有打开填空模式，自动打开。
                if (tbtnFullScreen.IsChecked != true)
                {
                    if (ckxForceFillBlankView.IsChecked != true) ckxForceFillBlankView.IsChecked = true;
                    ckxForceFillBlankView_Click(sender, e);
                }
            }
            else
            {
                timer.Stop();
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            SelectNextOrShowFillBlank();
            if (MainSelectedTitle != null)
            {
                //每个字给两秒时间，自动滚动显示。
                //设置计时器间隔时间
                SetTimerInterval();

            }
        }

        /// <summary>
        /// 设置计时器间隔时间。
        /// </summary>
        internal void SetTimerInterval()
        {
            string titleText = MainSelectedTitle.MainTextBlock.Text.Replace(" ", "").Replace("　", "").Replace("\t", "");
            string commentText = MainSelectedTitle.CommentTextBlock.Text.Replace(" ", "").Replace("　", "").Replace("\t", "");
            timer.Interval = new TimeSpan(0, 0, 2 * (titleText.Length + commentText.Length));
        }

        private void btnExitFullScreenOrAutoPresentation_Click(object sender, RoutedEventArgs e)
        {
            if (this.tbtnFullScreen.IsChecked == true)
            {
                this.tbtnFullScreen.IsChecked = false;
                tbtnFullScreen_Click(sender, e);
            }
            else if (this.ckxAutoPresentation.IsChecked == true)
            {
                this.ckxAutoPresentation.IsChecked = false;
                ckxAutoPresentation_Click(sender, e);
            }
        }

        private void miPresentateImage_Click(object sender, RoutedEventArgs e)
        {
            if (this.SelectedPictureBox != null)
            {
                this.SelectedPictureBox.PresentateImage();
                return;
            }

            if (this.SelectedTitleImage != null)
            {
                this.SelectedTitleImage.PresentateImage();
                return;
            }

            if (this.MainSelectedTitle != null &&
                this.MainSelectedTitle.ImgStackPanel.Children.Count > 0)
            {
                this.MainSelectedTitle.PresentateImages();
                return;
            }

            MessageBox.Show("　　请选定一个图片框或标题中的图像。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
        }

        internal TitleImage GetTitleImageByID(string id)
        {
            if (string.IsNullOrEmpty(id)) return null;

            foreach (var child in this.mainCanvas.Children)
            {
                Title title = child as Title;
                if (title == null) continue;

                foreach (var tii in title.ImgStackPanel.Children)
                {
                    TitleImage ti = tii as TitleImage;
                    if (ti != null && ti.ID == id) return ti;
                }
            }

            return null;
        }

        private void miFullScreen_Click(object sender, RoutedEventArgs e)
        {
            if (tbtnFullScreen.IsChecked != true)
            {
                tbtnFullScreen.IsChecked = true;
                tbtnFullScreen_Click(sender, e);
            }
        }

        private void btnEditorQuestionsOfTitle_Click(object sender, RoutedEventArgs e)
        {
            EditTitleBackgroundQuestionsText();
        }

        public void EditTitleBackgroundQuestionsText()
        {
            if (this.MainSelectedTitle == null)
            {
                MessageBox.Show("　　请选中一个标题。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            QuestionEditorWindow qew = new QuestionEditorWindow(this.MainSelectedTitle) { Owner = this, };
            qew.ShowDialog();
        }

        private void miPresentateQuestions_Click(object sender, RoutedEventArgs e)
        {
            if (this.MainSelectedTitle == null)
            {
                MessageBox.Show("　　请选定一个标题，此标题中应预先编辑了试题文本。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (string.IsNullOrEmpty(this.MainSelectedTitle.QuestionText))
            {
                MessageBox.Show("　　这个标题中没有提供试题文本。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            //自带验证。
            this.MainSelectedTitle.PresentateQuestions();
        }

        private void mainCanvas_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            if (ckxAutoPresentation.IsChecked == true)
            {
                //进入编辑状态时会自动退出“计时滚动”状态。
                ckxAutoPresentation.IsChecked = false;
                menuLine.Height = new GridLength(20);
                toolBarLine.Height = new GridLength(0, GridUnitType.Auto);
                statusLine.Height = new GridLength(32);
                bottomGridSplitter.Height = 6;

                ckxAutoPresentation_Click(null, null);
            }

            if (tbtnFullScreen.IsChecked != false) return;//全屏状态下禁止“输入文本即编辑”功能。

            if (SelectedComment != null)
            {
                Commands.EditCommentTextCommand.EditComment(e.Text);
                return;
            }

            if (SelectedRelation != null)
            {
                Commands.EditCommentTextCommand.EditRelationText(e.Text);
                return;
            }
            else if (SelectedTitlesList.Count > 0)
            {
                Commands.EditTitleTextCommand.EditTitleTextOfSwitchCommentVisibleWhenFullScreen(e.Text);
                return;
            }
        }

        private void miOutputDocumentText_Click(object sender, RoutedEventArgs e)
        {
            if (this.RootTitle == null)
            {
                MessageBox.Show("未找到根标题。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var sb = new StringBuilder();

            OutputTitleText(this.RootTitle, "", ref sb, false);
            sb.Append("\r\n");

            List<Title> subRoots = this.RootTitle.GetLowerLevelTitles();

            if (subRoots.Count <= 0)
            {
                try
                {
                    Clipboard.SetData(DataFormats.UnicodeText, sb.ToString());
                    MessageBox.Show("已将文档中所有标题的文本提取到剪贴板！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("向剪贴板写入文本时，可能与其它应用程序发生冲突（例如迅雷会监视剪贴板）！\r\n" + ex.Message,
                        Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                }
                return;
            }

            if (view == ViewMode.Default)
            {
                var leftSubRoots = new List<Title>();
                var rightSubRoots = new List<Title>();

                foreach (var t in subRoots)
                {
                    if (t.Direction == TitleDirection.Right)
                    {
                        rightSubRoots.Add(t);
                    }
                    else
                    {
                        leftSubRoots.Add(t);
                    }
                }

                foreach (var t in rightSubRoots)
                {
                    OutputTitleText(t, "", ref sb, true);
                    sb.Append("\r\n");
                }

                foreach (var t in leftSubRoots)
                {
                    OutputTitleText(t, "", ref sb, true);
                    sb.Append("\r\n");
                }
            }
            else
            {
                var allSubRoots = subRoots;
                foreach (var t in allSubRoots)
                {
                    OutputTitleText(t, "", ref sb, true);
                    sb.Append("\r\n");
                }
            }

            try
            {
                Clipboard.SetData(DataFormats.UnicodeText, sb.ToString());
                MessageBox.Show("已将文档中所有标题的文本提取到剪贴板！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show("向剪贴板写入文本时，可能与其它应用程序发生冲突（例如迅雷会监视剪贴板）！\r\n" + ex.Message,
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            return;
        }

        /// <summary>
        /// 提取所有标题的文本，这样可以方便地生成一个纯文本的提纲。
        /// ★只涉及Title，不涉及Comments/PictureBoxs/FixedTitles/Relations。
        /// </summary>
        /// <param name="t">要提取文本的标题。提取的文本中包含Title内部的Comment文本。</param>
        /// <param name="prefixText">前缀文本。例如一个全角空格。这个用于使文字提纲分层。</param>
        /// <param name="sb">字符串缓存。ref形式调用，存放提取结果。</param>
        /// <param name="outputLowerTitles">是否提取下级标题的文本。</param>
        internal void OutputTitleText(Title t, string prefixText, ref StringBuilder sb, bool outputLowerTitles)
        {
            var textSetNode = t.XmlData.SelectSingleNode("TextSet");
            if (textSetNode == null)
            {
                sb.Append("此标题未找到Xml.TextSet节点……\r\n");
                return;
            }

            sb.Append(prefixText);

            sb.Append(t.AutoNumberString);
            //sb.Append(textSetNode.InnerText);

            foreach (XmlNode node in textSetNode.ChildNodes)
            {
                if (node.InnerText.Replace(" ", "").Length == 0) sb.Append("\r\n");
                else sb.Append(node.InnerXml);
            }

            sb.Append("\r\n");

            var commentSetNode = t.XmlData.SelectSingleNode("CommentSet");
            if (commentSetNode != null && string.IsNullOrEmpty(commentSetNode.InnerText) == false)
            {
                sb.Append(prefixText + "　◆◆");

                //sb.Append(commentSetNode.InnerText);

                foreach (XmlNode node in commentSetNode.ChildNodes)
                {
                    if (node.InnerText.Replace(" ", "").Length == 0) sb.Append("\r\n");
                    else sb.Append(node.InnerXml);
                }

                sb.Append("\r\n");
            }

            if (outputLowerTitles)
            {
                prefixText += "　";

                var lowers = t.GetLowerLevelTitles();
                if (lowers.Count > 0)
                {
                    foreach (var lowerTitle in lowers)
                    {
                        OutputTitleText(lowerTitle, prefixText, ref sb, outputLowerTitles);
                    }
                }
            }
        }

        private void PresentationShadow_MouseDown(object sender, MouseButtonEventArgs e)
        {
            PresentationShadow.Visibility = System.Windows.Visibility.Collapsed;
        }

        #region 提问学生
        private void AskSomeoneAQuestion()
        {
            var student = this.GetAStudent();
            if (student == null)
            {
                tbkStudentName.Text = "<未设置名册>";
            }
            else
            {
                tbkStudentName.Text = student.Number + "号 " + student.Name;
            }

            askPanel.Visibility = System.Windows.Visibility.Visible;

            if (this.MainSelectedTitle != null)
            {
                Point basePt = new Point(0, 0);
                Point ltOfTitle = this.MainSelectedTitle.TranslatePoint(basePt, this.mainScrollViewer);
                Point brOfTitle = this.MainSelectedTitle.TranslatePoint(basePt, this.mainScrollViewer);
                Point ltOfAskPanel = this.askPanel.TranslatePoint(basePt, this.mainScrollViewer);
                Point brOfAskPanel = this.askPanel.TranslatePoint(new Point(askPanel.ActualWidth, askPanel.ActualHeight), mainScrollViewer);
                Rect rOfAskPanel = new Rect(ltOfAskPanel, brOfAskPanel);
                if (rOfAskPanel.Contains(brOfTitle) || rOfAskPanel.Contains(ltOfTitle))
                {
                    TryScrollTitle(this.MainSelectedTitle, true);
                }
            }
        }


        private List<Student> students;

        private void ReadStudentsFromFile(string fileShortName)
        {
            if (students == null)
            {
                students = new List<Student>();
            }
            students.Clear();

            if (string.IsNullOrEmpty(fileShortName))
            {
                return;
            }
            //先看文件在何处。

            string studentListFileFullPath = Globals.pathOfUserDirectory + (Globals.pathOfUserDirectory.EndsWith("\\") ? "" : "\\") + "StudentsLists\\" + fileShortName;
            if (System.IO.File.Exists(studentListFileFullPath))
            {
                using (System.IO.StreamReader sr = new System.IO.StreamReader(studentListFileFullPath))
                {
                    while (!sr.EndOfStream) //读到结尾退出
                    {
                        string strLine = sr.ReadLine(); strLine = strLine.Trim();

                        if (strLine.Length == 0) continue;

                        if (strLine.StartsWith("//")) continue;//这个用作备注。

                        if (strLine.Contains("//"))
                        {
                            strLine = strLine.Substring(0, strLine.IndexOf("//"));
                        }

                        if (string.IsNullOrEmpty(strLine)) continue;

                        //将每一行拆分，分隔符就是char 数组中的字符
                        string[] stringPieces = strLine.Split(new char[] { '\t', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
                        //将拆分好的string[] 存入list
                        if (stringPieces.Length == 2)
                        {
                            students.Add(new Student() { Name = stringPieces[1], Number = stringPieces[0], });
                        }

                    }
                }
            }

            RandomStudents();
        }

        private void RandomStudents()
        {
            if (studentPoint >= students.Count)
            {
                studentPoint = 0;
            }

            //混淆。
            int[] random = L.getRandomIntArray(students.Count);
            foreach (int i in random)
            {
                students[i].OrderNumber = random[i];
            }
            students.Sort(new CmparsionStudent());

        }

        private int studentPoint = 0;

        private Student GetAStudent()
        {
            if (students == null || students.Count <= 0) return null;

            var student = students[studentPoint];

            studentPoint++;

            //重新混淆一次。
            if (studentPoint >= students.Count)
            {
                RandomStudents();
                studentPoint = 0;
            }

            return student;
        }
        #endregion

        private void cmboxStudentsListFilenames_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selItem = cmboxStudentsListFilenames.SelectedItem as ComboBoxItem;
            if (selItem == null) return;

            string fileShortName = selItem.Content as string;
            if (string.IsNullOrEmpty(fileShortName)) return;

            ReadStudentsFromFile(fileShortName);

            //保存班级列表中当前选定项
            if (this.cmboxStudentsListFilenames.SelectedItem != null)
            {
                Globals.MainWindow.ConfigManager.Set("SelectedStudentListShortFileName", (this.cmboxStudentsListFilenames.SelectedItem as ComboBoxItem).Content.ToString());
            }
        }

        private void askPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            askPanel.Visibility = Visibility.Collapsed;
        }

        private void ckxForceFillBlankView_Click(object sender, RoutedEventArgs e)
        {
            if (tbtnFullScreen.IsChecked != false) return;
            //全屏演示状态下，似乎不应支持切换强制填空模式（强制填空模式只在非演示模式下有效）。
            //全屏模式本就支持填空。但支持PageUp/PageDown自动显示填空项。

            bool isFillBlankVisible = !(ckxForceFillBlankView.IsChecked == true);

            //显示所有Title的Comment//2013年10月6日改为显示填空项文本。令标题直接支持“填空模式”。
            foreach (UIElement ue in mainCanvas.Children)
            {
                var title = ue as Title;
                if (title == null)
                {
                    var comment = ue as Comment;
                    if (comment != null)
                    {
                        comment.IsFillBlankVisible = isFillBlankVisible;
                    }

                    continue;
                }

                title.IsFillBlankVisible = isFillBlankVisible;
            }

            this.ConfigManager.Set("FillBlankMode", (ckxForceFillBlankView.IsChecked == true).ToString());
        }

        private void btnOpenStudentListFolder_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (System.IO.Directory.Exists(Globals.pathOfStudentsList) == false)
                {
                    System.IO.Directory.CreateDirectory(Globals.pathOfStudentsList);
                }

                var files = new System.IO.DirectoryInfo(Globals.pathOfStudentsList).GetFiles();
                if (files.Length == 0)
                {
                    System.IO.File.WriteAllText(Globals.pathOfStudentsList + "SampleClass.txt", Properties.Resources.SampleClass);
                }

                System.Diagnostics.Process.Start("explorer.exe", $"\"{Globals.pathOfStudentsList}\"");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName);
            }
        }

        private void InCommonUseImages_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (tcRightTools.SelectedItem == tiInCommonUseImages)
                System.Diagnostics.Process.Start("explorer.exe", $"\"{Globals.pathOfInCommonUseImages}\"");
            else
            {
                tcRightTools.SelectedItem = tiInCommonUseImages;
            }
            e.Handled = true;
        }

        private void mainScrollViewer_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            //这个事件和PreviewMouseLeftButtonDown事件哪个在前？答案：此事件晚于PreviewMouseLeftButtonDown。

            //停用定时器，从自动滚动返回编辑。这应该放在最前头。
            if (ckxAutoPresentation.IsChecked == true)
            {
                ckxAutoPresentation.IsChecked = false;
                ckxAutoPresentation_Click(sender, e);

                //显示状态栏。
                if (menuLine.Height.Value <= 0)
                {
                    SwitchMenuBarAndStatusBar();
                }

                e.Handled = true;
                return;
            }

        }

        private void miShowWholeTitleFillBlanks_Click(object sender, RoutedEventArgs e)
        {
            miShowWholeTitleFillBlanks.IsChecked = !miShowWholeTitleFillBlanks.IsChecked;

            ConfigManager.Set("ShowWholeTitleFillBlanks", miShowWholeTitleFillBlanks.IsChecked ? "True" : "False");
        }

        private void miFillBlankTitleCountFromCurrentTitle_Click(object sender, RoutedEventArgs e)
        {
            if (MainSelectedTitle == null) return;

            int fillBlankTitleCount = 0;
            int titlesCount = 0;

            CountFillBlankTitles(MainSelectedTitle, ref fillBlankTitleCount, ref titlesCount);

            LunarMsgBox.MsgBoxShow(Globals.AppName, "【" + fillBlankTitleCount + "】个标题带填空项。",
                "　　此分支下共有标题〖" + titlesCount + "〗个。其中带填空项的有【" + fillBlankTitleCount + "】个。");
        }

        private void CountFillBlankTitles(Title title, ref int fillBlankTitlesCount, ref int titlesCount)
        {
            if (title == null) return;

            titlesCount++;
            if (title.HasFillBlank) fillBlankTitlesCount++;

            var lowerTitles = title.GetLowerLevelTitles();
            if (lowerTitles == null || lowerTitles.Count <= 0) return;

            foreach (var lowerTitle in lowerTitles)
            {
                CountFillBlankTitles(lowerTitle, ref fillBlankTitlesCount, ref titlesCount);
            }
        }

        private void tcTools_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (tcRightTools.SelectedItem == tiRecentFiles)
            {
                svRecentFiles.Focus();
            }
        }

        private void svRecentFiles_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = true;

            RecentFileItem curSelRecentFileItem = null;
            foreach (UIElement ue in spRecentFiles.Children)
            {
                RecentFileItem rfi = ue as RecentFileItem;
                if (rfi == null) continue;

                if (rfi.IsChecked)
                {
                    curSelRecentFileItem = rfi; break;
                }
            }

            if (curSelRecentFileItem == null)
            {
                foreach (UIElement ue in spRecentFiles.Children)
                {
                    RecentFileItem rfi = ue as RecentFileItem;
                    if (rfi == null) continue;

                    //选定第一个，结束。
                    rfi.IsChecked = true;
                    return;//不用break;
                }
            }

            if (curSelRecentFileItem == null) return;

            int index = spRecentFiles.Children.IndexOf(curSelRecentFileItem);
            if (index < 0) return;

            switch (e.Key)
            {
                case Key.Up:
                    {
                        if (index > 0)
                        {
                            RecentFileItem r = spRecentFiles.Children[index - 1] as RecentFileItem;
                            if (r != null)
                            {
                                r.IsChecked = true;
                                TryToShowRecentFileItem(r);
                            }
                        }
                        break;
                    }
                case Key.Down:
                    {
                        if (index < spRecentFiles.Children.Count - 1)
                        {
                            RecentFileItem r = spRecentFiles.Children[index + 1] as RecentFileItem;
                            if (r != null)
                            {
                                r.IsChecked = true;
                                TryToShowRecentFileItem(r);
                            }
                        }
                        break;
                    }
                case Key.Enter:
                    {
                        if (curSelRecentFileItem != null)
                        {
                            bool isShift = ((Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0);
                            curSelRecentFileItem.OpenDocument(isShift);
                        }
                        break;
                    }
            }


        }

        private void TryToShowRecentFileItem(RecentFileItem r)
        {
            if (r == null || this.spRecentFiles.Children.Contains(r) == false) return;

            if (r.ActualHeight > svRecentFiles.ActualHeight ||
                r.ActualWidth > svRecentFiles.ActualWidth)
                return;

            double vOffcet = svRecentFiles.VerticalOffset;
            double hOffcet = svRecentFiles.HorizontalOffset;

            Point ptTopLeft = new Point(0, 0);
            Point ptTopRight = new Point(r.ActualWidth, 0);
            Point ptBottomLeft = new Point(0, r.ActualHeight);
            Point ptBottomRight = new Point(r.ActualWidth, r.ActualHeight);

            Point rTopLeft = r.TranslatePoint(ptTopLeft, svRecentFiles);
            //if (rTopLeft.X < 0)
            //{
            //    svRecentFiles.ScrollToHorizontalOffset(hOffcet + rTopLeft.X);
            //}
            //else
            //{
            //    if (rTopLeft.X + r.ActualWidth > svRecentFiles.ActualWidth - 20)
            //    {
            //        svRecentFiles.ScrollToHorizontalOffset(hOffcet +
            //            rTopLeft.X + r.ActualWidth - (svRecentFiles.ActualWidth - 20));
            //    }

            //}

            if (rTopLeft.Y < 0)
            {
                svRecentFiles.ScrollToVerticalOffset(vOffcet + rTopLeft.Y);
            }
            else
            {
                if (rTopLeft.Y + r.ActualHeight > svRecentFiles.ActualHeight - 20)
                {
                    svRecentFiles.ScrollToVerticalOffset(vOffcet +
                        rTopLeft.Y + r.ActualHeight - (svRecentFiles.ActualHeight - 20));
                }
            }

        }

        private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            //部分快捷键应当是窗口级的
            bool isShift = false;

            KeyStates lShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates rShift = Keyboard.GetKeyStates(Key.RightShift);

            bool isCtrl = false;

            if ((lShift & KeyStates.Down) > 0 || (rShift & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            KeyStates lCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates rCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            if ((lCtrl & KeyStates.Down) > 0 || (rCtrl & KeyStates.Down) > 0)
            {
                isCtrl = true;
            }

            switch (e.Key)
            {
                case Key.T:
                    {
                        if (isCtrl && !isShift)
                        {
                            miSwitchCustomTimerOnOff_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.PageDown:
                    {
                        //if (tbtnFullScreen.IsChecked == true)
                        //{
                        //全屏状态下，Tab/Enter/PageUp/PageDown这四个键的行为有所不同。
                        //下一个标题。如果有下级，则取下级第一个。如果没有下级取上级的兄弟
                        //选取下一个。

                        SelectNextOrShowFillBlank();
                        //}

                        e.Handled = true;
                        break;
                    }
                case Key.PageUp:
                    {
                        //if (tbtnFullScreen.IsChecked == true)
                        //{
                        //全屏状态下，Tab/Enter/PageUp/PageDown这四个键的行为有所不同。
                        //如果有同级上一个，取之，如果没有同级上一个，取上级

                        this.SelectPreview();
                        //}

                        e.Handled = true;
                        break;
                    }
                case Key.F12:
                    {
                        if (isShift == false)
                        {
                            if (isCtrl == false)
                            {
                                btnSwitchRightToolPadVisible_Click(sender, e);
                            }
                        }

                        e.Handled = true;
                        break;
                    }
                case Key.OemMinus://缩小
                    {
                        if (isCtrl && isShift == false)
                        {
                            if (sliderOfCanvasScale.Value - 1 >= sliderOfCanvasScale.Minimum)
                            {
                                sliderOfCanvasScale.Value -= 1;
                            }
                        }
                        break;
                    }
                case Key.OemPlus://放大
                    {
                        if (isCtrl && isShift == false)
                        {
                            if (sliderOfCanvasScale.Value + 1 <= sliderOfCanvasScale.Maximum)
                            {
                                sliderOfCanvasScale.Value += 1;
                            }
                        }
                        break;
                    }
                case Key.Clear:
                    {
                        //将选定标题居中显示
                        if (this.MainSelectedTitle != null)
                        {
                            TryScrollTitle(this.MainSelectedTitle);
                        }

                        break;
                    }
                case Key.D0://还原
                    {
                        if (isCtrl && isShift == false)
                        {
                            if (view != ViewMode.Default)
                            {
                                sliderOfCanvasScale.Value = 0;
                            }
                            else
                            {
                                //当默认视图时，存在两种可能性：①用户打算恢复100%缩放比例；②取消标题缩进层级。

                                if (MainSelectedTitle != null && MainSelectedTitle.IsRootTitle == false && MainSelectedTitle.Indent != 0)
                                {
                                    Commands.SetTitleIndentCommand.SetTitleIndent();
                                }
                                else
                                {
                                    sliderOfCanvasScale.Value = 0;
                                }
                            }
                        }
                        break;
                    }
                case Key.O:
                    {
                        if (isCtrl && isShift == false)
                        {
                            Commands.OpenDocumentCommand.OpenDocument();
                        }

                        break;
                    }
                case Key.F6:
                    {
                        miPresentateImage_Click(sender, e);
                        break;
                    }
                case Key.F7:
                    {
                        miPresentateQuestions_Click(sender, e);
                        break;
                    }
                case Key.F1:
                    {
                        if (isCtrl)
                        {
                            miHelp_Click(sender, e);
                        }
                        else
                        {
                            btnSwitchLeftToolPadVisible_Click(sender, e);
                        }
                        break;
                    }
                case Key.F5:
                    {
                        if (isCtrl)
                        {
                            miShowGraphicDemonstrator(sender, e);
                        }
                        else
                        {
                            // 先退出自动演示。
                            if (ckxAutoPresentation.IsChecked == true)
                            {
                                ckxAutoPresentation.IsChecked = false;
                                ckxAutoPresentation_Click(sender, e);
                                bottomGridSplitter.Height = 6;
                            }

                            if (tbtnFullScreen.IsChecked == true)
                            {
                                tbtnFullScreen.IsChecked = false;
                            }
                            else
                            {
                                tbtnFullScreen.IsChecked = true;
                            }

                            tbtnFullScreen_Click(sender, e);
                        }
                        break;
                    }
                case Key.F11:
                    {
                        if (tbtnFullScreen.IsChecked != true)
                        {
                            SwitchMenuBarAndStatusBar();
                        }
                        break;
                    }
                case Key.Escape:
                    {
                        //esc也用于退出全屏
                        if (tbtnFullScreen.IsChecked == true)
                        {
                            tbtnFullScreen.IsChecked = false;
                            tbtnFullScreen_Click(sender, e);
                        }
                        else
                        {
                            if (ckxAutoPresentation.IsChecked == true)
                            {
                                //退出计时演示状态。
                                ckxAutoPresentation.IsChecked = false;

                                menuLine.Height = new GridLength(20);
                                toolBarLine.Height = new GridLength(0, GridUnitType.Auto);
                                statusLine.Height = new GridLength(32);
                                bottomGridSplitter.Height = 6;

                                this.WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
                                this.mainScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
                                this.mainScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
                                this.baseBorder.Margin = new Thickness(10);
                                this.WindowState = System.Windows.WindowState.Normal;
                            }
                            else if (tbtnFullScreen.IsChecked != true)
                            {
                                SwitchMenuBarAndStatusBar();
                            }
                        }
                        break;
                    }
                case Key.F2:
                    {
                        if (tbtnFullScreen.IsChecked != true)
                        {
                            if (!isShift && !isCtrl)
                            {
                                if (ckxForceFillBlankView.IsChecked == true)
                                    ckxForceFillBlankView.IsChecked = false;
                                else ckxForceFillBlankView.IsChecked = true;

                                ckxForceFillBlankView_Click(sender, e);
                            }
                        }
                        break;
                    }
                case Key.F8:
                    {
                        if (!isShift)
                        {
                            if (isCtrl == false)
                            {
                                miShowWholeTitleFillBlanks_Click(sender, e);
                            }
                            else
                            {
                                miFillBlankTitleCountFromCurrentTitle_Click(sender, e);
                            }
                        }
                        break;
                    }
                case Key.F9:
                    {
                        if (tbtnFullScreen.IsChecked != true)
                        {
                            if (ckxAutoPresentation.IsChecked != true)
                            {
                                ckxAutoPresentation.IsChecked = true;
                                statusLine.Height =
                                    toolBarLine.Height =
                                    menuLine.Height = new GridLength(0);
                                rightToolGridColumn.Width = new GridLength(0);
                                bottomGridSplitter.Height = 0;

                                this.WindowStyle = System.Windows.WindowStyle.None;
                                this.mainScrollViewer.HorizontalScrollBarVisibility =
                                    this.mainScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
                                this.baseBorder.Margin = new Thickness(0);
                                this.WindowState = System.Windows.WindowState.Maximized;
                            }
                            else
                            {
                                ckxAutoPresentation.IsChecked = false;
                                menuLine.Height = new GridLength(20);
                                toolBarLine.Height = new GridLength(0, GridUnitType.Auto);
                                statusLine.Height = new GridLength(32);
                                bottomGridSplitter.Height = 6;

                                this.WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
                                this.mainScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
                                this.mainScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
                                this.baseBorder.Margin = new Thickness(10);
                                this.WindowState = System.Windows.WindowState.Normal;
                            }

                            ckxAutoPresentation_Click(null, null);
                        }
                        break;
                    }
                case Key.OemQuestion:
                    {
                        if (isCtrl && !isShift) miAutoAskForFillBlanks_Click(sender, e);
                        break;
                    }
            }
        }

        private void miAutoAskForFillBlanks_Click(object sender, RoutedEventArgs e)
        {
            miAutoAskForFillBlanks.IsChecked = !miAutoAskForFillBlanks.IsChecked;
            this.ConfigManager.Set("AutoAskForFillBlanks", miAutoAskForFillBlanks.IsChecked.ToString());

            if (tbtnFullScreen.IsChecked == true)
            {
                this.AutoAskTip.Visibility = miAutoAskForFillBlanks.IsChecked ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        private void miDefaultFontSizeLarge_Click(object sender, RoutedEventArgs e)
        {
            ModifingItem mi = new ModifingItem()
            {
                ModifingName = "更改默认字号",
                IsNeedRefreshTitleAndLinesLocation = true
            };

            Action act = new Action(null, -1, null, "DefaultFontSize", this.DefaultFontSize.ToString(),
                25.ToString(), ActionType.DefaultFontSize, null);

            this.DefaultFontSize = 25;

            mi.Add(act);
            this.IsModified = true;
            this.Do(mi);
            RefreshDefaultFontSizeMenuItemsState();
        }

        private void miDefaultFontSizeMiddle_Click(object sender, RoutedEventArgs e)
        {
            ModifingItem mi = new ModifingItem()
            {
                ModifingName = "更改默认字号",
                IsNeedRefreshTitleAndLinesLocation = true
            };

            Action act = new Action(null, -1, null, "DefaultFontSize", this.DefaultFontSize.ToString(),
                20.ToString(), ActionType.DefaultFontSize, null);

            this.DefaultFontSize = 20;

            mi.Add(act);
            this.IsModified = true;
            this.Do(mi);
            RefreshDefaultFontSizeMenuItemsState();
        }

        private void miDefaultFontSizeSmall_Click(object sender, RoutedEventArgs e)
        {
            ModifingItem mi = new ModifingItem()
            {
                ModifingName = "更改默认字号",
                IsNeedRefreshTitleAndLinesLocation = true
            };

            Action act = new Action(null, -1, null, "DefaultFontSize", this.DefaultFontSize.ToString(),
                15.ToString(), ActionType.DefaultFontSize, null);

            this.DefaultFontSize = 15;

            mi.Add(act);
            this.IsModified = true;
            this.Do(mi);
            RefreshDefaultFontSizeMenuItemsState();
        }

        private void AutoAskTip_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            miAutoAskForFillBlanks_Click(sender, e);
        }

        private DispatcherTimer dTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 1), };

        private Stopwatch sw = new Stopwatch();

        private void miSwitchCustomTimerOnOff_Click(object sender, RoutedEventArgs e)
        {
            miSwitchCustomTimerOnOff.IsChecked = !miSwitchCustomTimerOnOff.IsChecked;

            if (miSwitchCustomTimerOnOff.IsChecked)
            {
                sw.Start();
                dTimer.Tick += DTimer_Tick;
                dTimer.Start();

                TimeBorder.Visibility = Visibility.Visible;
            }
            else
            {
                StopCustomTimer();
            }
        }

        private void StopCustomTimer()
        {
            sw.Stop();
            dTimer.Stop();
            TimeBorder.Visibility = Visibility.Collapsed;
        }

        private void DTimer_Tick(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            TimeSpan ts = sw.Elapsed;

            if (ts.Hours > 0) sb.Append($"{ts.Hours}:");

            sb.Append($"{ts.Minutes}:");
            sb.Append($"{ts.Seconds}");

            TimeTextBlock.Text = sb.ToString();
        }

        private void TimeBorder_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            StopCustomTimer();
        }

        /// <summary>
        /// 用以快速输入文本/批量添加分层标题的窗口。
        /// </summary>
        private InsertTitlesQuicklyWindow iqw = null;

        private void miInsertLines_Click(object sender, RoutedEventArgs e)
        {
            if (iqw == null)
            {
                iqw = new InsertTitlesQuicklyWindow() { Owner = this, WindowStartupLocation = WindowStartupLocation.Manual, };
            }

            if (iqw.Visibility != Visibility.Visible) iqw.Show();
            if (iqw.IsActive == false) iqw.Activate();
        }

        private void miShowTutorial_Click(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Process.Start(Globals.tutorialPath);
        }

        /// <summary>
        /// 文档背景色是否浅色系。
        /// </summary>
        public bool IsLightDocumentBackground
        {
            get
            {
                var docBack = Globals.MainWindow.mainCanvas.Background;
                if (docBack is ImageBrush)
                {
                    // 只支持一种：浅花文背景色
                    return true;
                }
                else
                {
                    var bsh = docBack as SolidColorBrush;
                    if (bsh == null || Globals.IsLightColor(bsh.Color))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
        }

        private void maOutColorValue_Click(object sender, RoutedEventArgs e)
        {
            var colorNames = @"黑色,橙色,绿色,青色,蓝色,紫色,蓝紫罗兰色,棕色,坚树色,军校蓝,查特酒绿,巧克力色,珊瑚色,矢车菊蓝,猩红,深蓝,深青,深金菊色,深灰,深绿,深卡其布色,深洋红,深橄榄绿,深橙色,深兰花紫,深红,鲑鱼色,深海绿,深石板蓝,深石板灰,深宝石绿,深紫罗兰色,深粉色,深天蓝,暗灰,道奇蓝,耐火砖色,森林绿,灯笼海棠色,金色,金菊黄,灰色,热粉色,印度红,靛青,草坪绿,浅蓝,浅珊瑚色,浅灰,浅绿,浅粉色,浅鲑鱼肉色,浅海绿,浅海蓝,浅石板灰,浅钢蓝,酸橙色,酸橙绿,洋红,栗色,中碧玉绿,中蓝,中兰花紫,中紫,中海绿,中石板蓝,中春绿,中宝石绿,中紫罗兰红,午夜蓝,海军蓝,橄榄色,橄榄褐,橙色,兰花紫,灰紫罗兰红,秘鲁色,粉红,李色,粉蓝,玫瑰棕色,皇室蓝,重褐色,鲑鱼肉色,沙棕色,海绿,黄土赭,银白,天蓝,石板蓝,石板灰,春绿,钢蓝,茶色,水鸭绿,蓟色,番茄色,宝石绿,紫罗兰色,黄绿";

            var sb = new StringBuilder();
            foreach (var brushItem in BrushManager.BrushesList)
            {
                if (colorNames.Contains(brushItem.CnName))
                {
                    var bsh = brushItem.Brush as SolidColorBrush;
                    if (bsh == null) continue;

                    var color = bsh.Color;
                    sb.Append($"r:{color.R};g:{color.G};b:{color.B};{brushItem.CnName};{brushItem.EnName}\r\n");
                }
            }
            Clipboard.SetData(DataFormats.UnicodeText, sb.ToString());
            MessageBox.Show("已复制色彩值！");
        }

        private WorkspaceManager workspaceManager;

        public WorkspaceManager WorkspaceManager { get { return workspaceManager; } }

        private void btnLoadLmeWorkspaceStruct_Click(object sender, RoutedEventArgs e)
        {
            LoadOrCreateWorkspace();
        }

        private void LoadOrCreateWorkspace()
        {
            try
            {
                if (System.IO.File.Exists(Globals.MainWindow.FullPathOfDiskFile) == false)
                {
                    MessageBox.Show("磁盘上找不到当前文件。请先保存当前文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                var workspacePath = Globals.PathOfWorkspace;
                if (System.IO.Directory.Exists(workspacePath) == false || System.IO.File.Exists(workspacePath + "WorkspaceItems.xml") == false)
                {
                    var workspaceItemFilePath = Globals.PathOfParentDirectory + "WorkspaceItems.xml";

                    if (System.IO.File.Exists(workspaceItemFilePath) == false)
                    {
                        var result = MessageBox.Show("尚未创建与文件同名的工作区目录，当前文件所在目录也不是个 LME 工作区。操作无法继续。\r\n\r\n要自动创建一个新工作区吗？", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                        if (result != MessageBoxResult.Yes)
                            return;
                        System.IO.Directory.CreateDirectory(workspacePath);

                        result = MessageBox.Show("已自动创建同名目录。\r\n\r\n要尝试调用 Markdown To CHM(LME) 打开该目录作为工作区编写文档或添加资源吗？\r\n\r\n★Markdown To CHM(LME) 打开后，请重新执行本操作！！！", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                        if (result != MessageBoxResult.Yes)
                            return;
                        OpenWorkspace(workspacePath);
                        workspaceManager.LoadWorkspaceFromXml(workspacePath + "WorkspaceItems.xml");
                    }
                    else
                    {
                        workspaceManager.LoadWorkspaceFromXml(workspaceItemFilePath);
                    }

                    return;    // 要求用户重新点击此按钮！！！
                }

                workspaceManager.LoadWorkspaceFromXml(workspacePath + "WorkspaceItems.xml");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void OpenWorkspace(string workspacePath = null)
        {
            if (string.IsNullOrWhiteSpace(workspacePath))
            {
                workspacePath = Globals.PathOfWorkspace;
                if (string.IsNullOrWhiteSpace(workspacePath))
                {
                    LoadOrCreateWorkspace();
                    return;
                }
                else
                {
                    if (System.IO.File.Exists(workspacePath + "WorkspaceItems.xml") == false)
                    {
                        try
                        {
                            var pdi = new System.IO.DirectoryInfo(workspacePath);
                            var parentPath = pdi.Parent.FullName;
                            if (parentPath.EndsWith("\\") == false)
                                parentPath += "\\";

                            var parentWorkspaceItemsXmlPath = parentPath + "WorkspaceItems.xml";

                            if (System.IO.File.Exists(parentWorkspaceItemsXmlPath) == false)
                            {
                                LoadOrCreateWorkspace();
                                return;
                            }

                            workspacePath = parentPath;
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                            return;
                        }
                    }
                }
            }

            RegistryKey regSubKey;
            RegistryKey regKey = Registry.ClassesRoot;
            string strRegPath = @"directory\shell\用 Markdown To CHM 打开\command\";
            regSubKey = regKey.OpenSubKey(strRegPath);
            if (regSubKey == null)
            {
                MessageBox.Show("未能在此计算机上找到 Markdown To CHM(LME)，无法调用。", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var regValue = regSubKey.GetValue("");
            string lmeInstalledPath = regValue.ToString();
            if (lmeInstalledPath.EndsWith(" %1"))
                lmeInstalledPath = lmeInstalledPath.Substring(0, lmeInstalledPath.Length - 3);

            System.Diagnostics.Process.Start(lmeInstalledPath, $"\"{workspacePath}\"");

        }

        public void GetLmeWorkspaceItems(object item, ref List<WorkspaceTreeViewItem> list)
        {
            if (list == null || item == null) return;

            var wtvi = item as WorkspaceTreeViewItem;
            if (wtvi == null) return;

            list.Add(wtvi);

            foreach (var subItem in wtvi.Items)
            {
                var subwtvi = subItem as WorkspaceTreeViewItem;
                if (subwtvi == null) continue;

                GetLmeWorkspaceItems(subwtvi, ref list);
            }
        }

        /// <summary>
        /// 这个要支持一个操作包含多个步骤。
        /// </summary>
        /// <param name="imgFileFullPath"></param>
        /// <param name="linkText"></param>
        internal void InsertTitleImage(string imgFileFullPath = null, string linkText = null, string linkTitle = null)
        {
            if (string.IsNullOrEmpty(linkText) || string.IsNullOrEmpty(linkTitle))
            {
                MessageBox.Show("传入的资源路径或标题文本为空！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            try
            {
                var fi = new System.IO.FileInfo(this.FullPathOfDiskFile);
                var workspacePath = fi.FullName.Substring(0, fi.FullName.Length - fi.Extension.Length);
                if (linkText.ToLower().StartsWith(workspacePath.ToLower()))
                {
                    linkText = linkText.Substring(workspacePath.Length);
                }
                else
                {
                    var parentDI = new System.IO.FileInfo(FullPathOfDiskFile).Directory;
                    var parentDirectoryPath = parentDI.FullName.ToLower();

                    if (linkText.ToLower().StartsWith(parentDirectoryPath))
                    {
                        linkText = linkText.Substring(parentDirectoryPath.Length);
                    }
                }

                if (Globals.MainWindow.SelectedTitleImage != null)
                {
                    //先取出当前选中的图片索引。
                    Title curTitle = Globals.MainWindow.SelectedTitleImage.MasterTitle;
                    TitleImage ti = new TitleImage(Globals.MainWindow, curTitle);

                    XmlNode imgSetNode = curTitle.XmlData.SelectSingleNode("ImageSet");
                    if (imgSetNode == null)
                    {
                        imgSetNode = curTitle.XmlData.AppendXml("<ImageSet/>");
                    }

                    int index = curTitle.ImgStackPanel.Children.IndexOf(Globals.MainWindow.SelectedTitleImage);

                    XmlNode newImgNode = imgSetNode.InsertXmlAt(Properties.Resources.TitleImageOutXml, index);
                    if (linkText.EndsWith("\\") || System.IO.Directory.Exists(linkText))
                    {
                        var di = new System.IO.DirectoryInfo(linkText);
                        linkText = linkText + "_" + di.Name + ".md";
                    }

                    newImgNode.SetAttribute("Link", linkText);
                    if (linkText.ToLower().EndsWith(".md") || linkText.ToLower().EndsWith(".html"))
                    {
                        newImgNode.SetAttribute("LinkType", "Html");
                    }
                    else
                    {
                        newImgNode.SetAttribute("LinkType", "Image");
                    }
                    newImgNode.SetAttribute("LinkTitle", linkTitle);

                    ti.XmlData = newImgNode;

                    ModifingItem mi = new ModifingItem()
                    {
                        IsNeedRefreshTitleAndLinesLocation = true,
                        ModifingName = "插入资源链接图标"
                    };

                    Action actInsertTitleImage = new Action(ti.ID, index,
                        ti.GetType().Name, "InsertTitleImage",
                        null, ti.XmlData.OuterXml, ActionType.InsertTitleImage, curTitle.ID);

                    curTitle.ImgStackPanel.Children.Insert(index, ti);
                    mi.Add(actInsertTitleImage);

                    mi.OldSelectedTitleID = curTitle.ID;
                    mi.NewSelectedTitleID = curTitle.ID;//如果用TitleImage的ID会很难做。
                    mi.OldSelectedTitleImageID = Globals.MainWindow.SelectedTitleImage.ID;
                    mi.NewSelectedTitleImageID = ti.ID;

                    ti.IsSelected = true;
                    Globals.MainWindow.SelectedTitleImage = ti;

                    if (imgFileFullPath != null)
                    {
                        ti.LoadImageFromFile(mi, imgFileFullPath);
                    }
                    else
                    {
                        curTitle.Editor.Do(mi);
                        curTitle.Editor.IsModified = true;
                        curTitle.Editor.RefreshTitleAndLinesLocation();
                    }
                }
                else
                {
                    if (Globals.SelectedTitlesList.Count <= 0) return;

                    //这种情况，添加到末尾好了。

                    Title curTitle = null;

                    foreach (Title t in Globals.SelectedTitlesList)
                    {
                        if (t.IsMainSelectedTitle)
                        {
                            curTitle = t;
                            break;
                        }
                    }

                    if (curTitle == null)
                    {
                        MessageBox.Show("　　发生意外，找不到插入标题图片的基准位置。\r\n" +
                            "　　请重新选取一个标题或标题图片作为插入新标题图片的基准位置！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }

                    TitleImage ti = new TitleImage(Globals.MainWindow, curTitle);

                    XmlNode imgSetNode = curTitle.XmlData.SelectSingleNode("ImageSet");
                    if (imgSetNode == null)
                    {
                        imgSetNode = curTitle.XmlData.AppendXml("<ImageSet/>");
                    }

                    XmlNode newImgNode = imgSetNode.AppendXml(Properties.Resources.TitleImageOutXml);
                    if (linkText.EndsWith("\\") || System.IO.Directory.Exists(linkText))
                    {
                        var di = new System.IO.DirectoryInfo(linkText);
                        linkText = linkText + "_" + di.Name + ".md";
                    }

                    newImgNode.SetAttribute("Link", linkText);
                    if (linkText.ToLower().EndsWith(".md") || linkText.ToLower().EndsWith(".html"))
                    {
                        newImgNode.SetAttribute("LinkType", "Html");
                    }
                    else
                    {
                        newImgNode.SetAttribute("LinkType", "Image");
                    }
                    newImgNode.SetAttribute("LinkTitle", linkTitle);

                    ti.XmlData = newImgNode;

                    ModifingItem mi = new ModifingItem()
                    {
                        IsNeedRefreshTitleAndLinesLocation = true,
                        ModifingName = "插入资源链接图标"
                    };

                    Action actInsertTitleImage = new Action(ti.ID,
                        curTitle.ImgStackPanel.Children.Count, ti.GetType().Name, "InsertTitleImage",
                        null, ti.XmlData.OuterXml, ActionType.InsertTitleImage, curTitle.ID);

                    curTitle.ImgStackPanel.Children.Add(ti);
                    mi.Add(actInsertTitleImage);
                    mi.OldSelectedTitleID = mi.NewSelectedTitleID = curTitle.ID;
                    mi.NewSelectedTitleImageID = ti.ID;

                    ti.IsSelected = true;
                    Globals.MainWindow.SelectedTitleImage = ti;

                    if (imgFileFullPath != null)
                    {
                        ti.LoadImageFromFile(mi, imgFileFullPath);
                    }
                    else
                    {
                        curTitle.Editor.Do(mi);
                        curTitle.Editor.IsModified = true;
                        curTitle.Editor.RefreshTitleAndLinesLocation();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miShowGraphicDemonstrator(object sender, RoutedEventArgs e)
        {
            var titles = GetAllTitles();
            var groups = new List<DemoResourceGroup>();
            foreach (var title in titles)
            {
                var linkImages = title.ResourceLinkImages;
                if (linkImages != null && linkImages.Count > 0)
                {
                    var group = new DemoResourceGroup();
                    group.Title = title.XmlData.InnerText.Trim(new char[] { '\r', '\n', ' ', '　', '\t', });
                    foreach (var li in linkImages)
                    {
                        group.Add(new DemoResourceInfo()
                        {
                            RelativePath = li.Link,
                            LinkType = li.LinkType,
                            LinkTitle = li.LinkTitle,
                        });
                    }
                    if (group.Count > 0)
                        groups.Add(group);
                }
            }

            if (groups.Count <= 0)
            {
                MessageBox.Show("当前文档中没有以标题内置图标的方式添加对资源的引用，无法继续演示。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var win = new GraphicDemonstrator(FormatDemoResourceGroups(groups)) { Owner = this, };
            win.ShowDialog();
        }

        private List<DemoResourceGroup> FormatDemoResourceGroups(List<DemoResourceGroup> groups)
        {
            if (groups == null || groups.Count <= 0) return null;

            var newGroups = new List<DemoResourceGroup>();
            foreach (var gp in groups)
            {
                if (gp.Count <= 0) continue;
                if (gp.Count <= 6)
                {
                    newGroups.Add(gp);
                    continue;
                }

                DemoResourceGroup newGroup = null;
                for (int i = 0; i < gp.Count; i++)
                {
                    var ri = gp.Resources[i];
                    if (i % 6 == 0)
                    {
                        newGroup = new DemoResourceGroup() { Title = gp.Title, };
                        newGroup.Add(ri);
                        newGroups.Add(newGroup);
                        continue;
                    }
                    else
                    {
                        newGroup?.Add(ri);
                    }
                }
            }

            return newGroups;
        }

        private void btnOpenWorkspace_Click(object sender, RoutedEventArgs e)
        {
            OpenWorkspace();
        }

        private void miAddLink_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.Items.Count <= 0) return;

            var activeWtiv = tvWorkDirectory.Items[0] as WorkspaceTreeViewItem;
            if (activeWtiv == null) return;

            if (activeWtiv.IsSelected)
            {
                switch (activeWtiv.ItemType)
                {
                    case WorkspaceTreeViewItem.Type.File:
                    case WorkspaceTreeViewItem.Type.Folder:
                    case WorkspaceTreeViewItem.Type.Image:
                        {
                            InsertTitleImage(null, activeWtiv.FullPath, activeWtiv.Title);
                            break;
                        }
                }
            }
            else
            {
                activeWtiv = WorkspaceTreeViewItem.GetActiveWorkspaceTreeViewItem(activeWtiv);
                if (activeWtiv != null)
                {
                    switch (activeWtiv.ItemType)
                    {
                        case WorkspaceTreeViewItem.Type.File:
                        case WorkspaceTreeViewItem.Type.Folder:
                        case WorkspaceTreeViewItem.Type.Image:
                            {
                                InsertTitleImage(null, activeWtiv.FullPath, activeWtiv.Title);
                                break;
                            }
                    }
                }
                else
                {
                    MessageBox.Show("请选先选中某个工作区资源条目（文件、文件夹或图像——注意图像文件夹不可用！）", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
            }
        }

        private void Window_Activated(object sender, EventArgs e)
        {
            TryLoadWorkspace();
        }
    }
}
